├── .dockerignore ├── .github └── workflows │ ├── debricked.yml │ ├── docker.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.FoD.md ├── README.md ├── SECURITY.md ├── UPGRADE-2.0.md ├── assets ├── CLI_logo_1024.png ├── cli.png └── debricked_resolve.png ├── build └── docker │ ├── alpine.Dockerfile │ └── debian.Dockerfile ├── cmd └── debricked │ └── main.go ├── examples └── templates │ ├── Argo │ ├── README.md │ └── argo.yml │ ├── Azure │ ├── README.md │ ├── azure-pipelines-private-maven-repository.yml │ ├── azure-pipelines-private-nuget-repository.yml │ └── azure-pipelines.yml │ ├── Bitbucket │ ├── README.md │ └── bitbucket-pipelines.yml │ ├── BuildKite │ ├── README.md │ └── pipeline.yml │ ├── CircleCI │ ├── README.md │ └── config.yml │ ├── GitHub │ ├── README.md │ ├── debricked-non-docker.yml │ └── debricked.yml │ ├── GitLab │ ├── README.md │ └── gitlab-ci.yml │ ├── Jenkins │ ├── Jenkinsfile │ └── README.md │ ├── README.md │ └── Travis │ ├── README.md │ └── travis.yml ├── go.mod ├── go.sum ├── internal ├── auth │ ├── auth.go │ ├── auth_test.go │ ├── callback.go │ ├── callback_test.go │ └── testdata │ │ └── auth_mock.go ├── automation │ ├── rule.go │ ├── rule_test.go │ └── trigger.go ├── callgraph │ ├── README.md │ ├── cgexec │ │ ├── command.go │ │ ├── command_test.go │ │ ├── context.go │ │ ├── context_test.go │ │ └── testdata │ │ │ ├── command_mock.go │ │ │ └── context_mock.go │ ├── config │ │ └── config.go │ ├── finder │ │ ├── finder.go │ │ ├── golangfinder │ │ │ ├── finder.go │ │ │ ├── finder_test.go │ │ │ └── testdata │ │ │ │ ├── app.go │ │ │ │ ├── extrapackage │ │ │ │ └── extra.go │ │ │ │ ├── random_file.py │ │ │ │ └── util.go │ │ ├── javafinder │ │ │ ├── finder.go │ │ │ ├── finder_test.go │ │ │ ├── pomfinder.go │ │ │ ├── pomfinder_test.go │ │ │ └── testdata │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ ├── guava │ │ │ │ └── pom.xml │ │ │ │ ├── notAPom.xml │ │ │ │ ├── pom.xml │ │ │ │ └── test_project │ │ │ │ ├── excluded_folder │ │ │ │ └── excluded_file.txt │ │ │ │ └── included_folder │ │ │ │ └── included_file.txt │ │ ├── refiner.go │ │ ├── refiner_test.go │ │ └── testdata │ │ │ └── finder_mock.go │ ├── generation.go │ ├── generation_test.go │ ├── generator.go │ ├── generator_test.go │ ├── job │ │ ├── base_job.go │ │ ├── base_job_test.go │ │ ├── job.go │ │ └── testdata │ │ │ ├── job_mock.go │ │ │ └── job_test_util.go │ ├── language │ │ ├── golang │ │ │ ├── README.md │ │ │ ├── callgraph.go │ │ │ ├── callgraph_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── language.go │ │ │ ├── language_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── callgraph_mock.go │ │ │ │ └── fixture │ │ │ │ └── app.go │ │ ├── java │ │ │ ├── README.md │ │ │ ├── callgraph.go │ │ │ ├── callgraph_test.go │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── embedded │ │ │ │ ├── SootWrapper.jar │ │ │ │ └── gradle-script.groovy │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── language.go │ │ │ ├── language_test.go │ │ │ ├── soot-wrapper.jar │ │ │ ├── soot_handler.go │ │ │ ├── soot_handler_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── callgraph_mock.go │ │ │ │ └── cmd_factory_mock.go │ │ ├── language.go │ │ └── language_test.go │ ├── model │ │ ├── model.go │ │ └── model_test.go │ ├── scheduler.go │ ├── scheduler_test.go │ ├── strategy │ │ ├── strategy.go │ │ ├── strategy_factory.go │ │ ├── strategy_factory_test.go │ │ └── testdata │ │ │ ├── strategy_mock.go │ │ │ └── strategy_mock_factory.go │ └── testdata │ │ └── generator_mock.go ├── ci │ ├── argo │ │ ├── ci.go │ │ └── ci_test.go │ ├── azure │ │ ├── ci.go │ │ └── ci_test.go │ ├── bitbucket │ │ ├── ci.go │ │ └── ci_test.go │ ├── buildkite │ │ ├── ci.go │ │ └── ci_test.go │ ├── ci.go │ ├── circleci │ │ ├── ci.go │ │ └── ci_test.go │ ├── env │ │ └── env.go │ ├── github │ │ ├── ci.go │ │ └── ci_test.go │ ├── gitlab │ │ ├── ci.go │ │ └── ci_test.go │ ├── service.go │ ├── service_test.go │ ├── testdata │ │ ├── ci_assert.go │ │ └── env.go │ ├── travis │ │ ├── ci.go │ │ └── ci_test.go │ └── util │ │ ├── util.go │ │ └── util_test.go ├── client │ ├── client.go │ ├── deb_client.go │ ├── deb_client_test.go │ ├── request.go │ ├── request_test.go │ ├── retry.go │ ├── retry_test.go │ └── testdata │ │ ├── client │ │ └── client_mock.go │ │ └── deb_client_mock.go ├── cmd │ ├── auth │ │ ├── auth.go │ │ ├── auth_test.go │ │ ├── login │ │ │ ├── login.go │ │ │ └── login_test.go │ │ ├── logout │ │ │ ├── logout.go │ │ │ └── logout_test.go │ │ └── token │ │ │ ├── token.go │ │ │ └── token_test.go │ ├── callgraph │ │ ├── callgraph.go │ │ └── callgraph_test.go │ ├── cmderror │ │ └── error.go │ ├── files │ │ ├── files.go │ │ ├── files_test.go │ │ └── find │ │ │ ├── find.go │ │ │ └── find_test.go │ ├── fingerprint │ │ ├── fingerprint.go │ │ └── fingerprint_test.go │ ├── report │ │ ├── license │ │ │ ├── license.go │ │ │ └── license_test.go │ │ ├── report.go │ │ ├── report_test.go │ │ ├── sbom │ │ │ ├── sbom.go │ │ │ └── sbom_test.go │ │ ├── testdata │ │ │ └── reporter_mock.go │ │ └── vulnerability │ │ │ ├── vulnerability.go │ │ │ └── vulnerability_test.go │ ├── resolve │ │ ├── resolve.go │ │ └── resolve_test.go │ ├── root │ │ ├── root.go │ │ └── root_test.go │ └── scan │ │ ├── scan.go │ │ ├── scan_test.go │ │ └── testdata │ │ └── npm │ │ └── package.json ├── debug │ ├── debug.go │ └── debug_test.go ├── file │ ├── exclusion.go │ ├── exclusion_test.go │ ├── finder.go │ ├── finder_test.go │ ├── format.go │ ├── format_test.go │ ├── group.go │ ├── group_test.go │ ├── groups.go │ ├── groups_test.go │ ├── pcre │ │ ├── pcre.go │ │ └── pcre_test.go │ ├── testdata │ │ ├── composer │ │ │ ├── composer.json │ │ │ └── composer.lock │ │ ├── finder_mock.go │ │ ├── go │ │ │ └── go.mod │ │ ├── misc │ │ │ ├── Cargo.lock │ │ │ ├── composer.json │ │ │ ├── composer.lock │ │ │ ├── debricked-config.yaml │ │ │ ├── go.mod │ │ │ ├── package.json │ │ │ ├── requirements.txt │ │ │ └── zipped.zip │ │ ├── pip │ │ │ ├── requirements-dev.txt │ │ │ ├── requirements-dev.txt.pip.debricked.lock │ │ │ ├── requirements.test.txt │ │ │ ├── requirements.txt │ │ │ └── requirements.txt.pip.debricked.lock │ │ ├── test │ │ │ └── test-file │ │ ├── workspace │ │ │ ├── common │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── packages │ │ │ │ │ ├── package_one │ │ │ │ │ └── package.json │ │ │ │ │ └── package_two │ │ │ │ │ └── package.json │ │ │ └── rare │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── packages │ │ │ │ ├── package_one │ │ │ │ └── package.json │ │ │ │ └── package_two │ │ │ │ └── package.json │ │ └── yarn │ │ │ └── yarn.lock │ ├── workspace.go │ └── workspace_test.go ├── fingerprint │ ├── exclusion.go │ ├── fingerprint.go │ ├── fingerprint_test.go │ └── testdata │ │ ├── archive │ │ ├── bz2 │ │ │ └── stuf-0.1.tar.bz2 │ │ ├── jar │ │ │ └── log4j-api-2.18.0.jar │ │ ├── nupkg │ │ │ └── newtonsoft.json.13.0.3.nupkg │ │ ├── tgz │ │ │ └── lodash.tgz │ │ └── whl │ │ │ └── requests-2.31.0-py3-none-any.whl │ │ ├── fingerprinter │ │ ├── nofile.txt │ │ ├── something.resx │ │ ├── subdirectory │ │ │ └── mvnw.cmd │ │ ├── testfile.py │ │ └── wfailing.jar │ │ └── fingerprinter_mock.go ├── git │ ├── git.go │ ├── git_test.go │ ├── meta_object.go │ └── meta_object_test.go ├── io │ ├── archive.go │ ├── archive_test.go │ ├── err │ │ ├── error.go │ │ ├── errors.go │ │ └── errors_test.go │ ├── file_system.go │ ├── file_system_test.go │ ├── file_writer.go │ ├── file_writer_test.go │ ├── testdata │ │ ├── archive_mock.go │ │ ├── embed-file │ │ ├── file_system_mock.go │ │ ├── file_writer_mock.go │ │ ├── text.txt │ │ ├── text.zip │ │ └── zip_mock.go │ ├── zip.go │ └── zip_test.go ├── report │ ├── license │ │ ├── report.go │ │ └── report_test.go │ ├── report.go │ ├── sbom │ │ ├── report.go │ │ └── report_test.go │ └── vulnerability │ │ ├── report.go │ │ └── report_test.go ├── resolution │ ├── file │ │ ├── file_batch.go │ │ ├── file_batch_factory.go │ │ ├── file_batch_factory_test.go │ │ ├── file_batch_test.go │ │ └── testdata │ │ │ ├── file_batch_factory_mock.go │ │ │ └── file_batch_mock.go │ ├── job │ │ ├── base_job.go │ │ ├── base_job_test.go │ │ ├── error.go │ │ ├── error_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── job.go │ │ └── testdata │ │ │ ├── job_mock.go │ │ │ └── job_test_util.go │ ├── pm │ │ ├── bower │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ └── cmd_factory_mock.go │ │ ├── composer │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ └── cmd_factory_mock.go │ │ ├── gomod │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ └── cmd_factory_mock.go │ │ ├── gradle │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── gradle-init │ │ │ │ └── gradle-init-script.groovy │ │ │ ├── init_script_handler.go │ │ │ ├── init_script_handler_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── meta_file_finder.go │ │ │ ├── meta_file_finder_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── project.go │ │ │ ├── setup.go │ │ │ ├── setup_err.go │ │ │ ├── setup_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ └── project │ │ │ │ ├── build.gradle │ │ │ │ ├── gradlew │ │ │ │ ├── gradlew.bat │ │ │ │ ├── settings.gradle │ │ │ │ └── subproject │ │ │ │ └── build.gradle │ │ ├── maven │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── pom_service.go │ │ │ ├── pom_service_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ ├── guava │ │ │ │ └── pom.xml │ │ │ │ ├── invalidPom.xml │ │ │ │ ├── notAPom.xml │ │ │ │ ├── pom.xml │ │ │ │ └── pom_service_mock.go │ │ ├── npm │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ └── cmd_factory_mock.go │ │ ├── nuget │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ ├── empty_cmd_factory_mock.go │ │ │ │ ├── invalid │ │ │ │ └── packages.config │ │ │ │ ├── invalid_dependency │ │ │ │ └── packages.config │ │ │ │ ├── missing_framework │ │ │ │ ├── packages.config │ │ │ │ └── packages.config.nuget.debricked.csproj.temp │ │ │ │ └── valid │ │ │ │ └── packages.config │ │ ├── pip │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ ├── crlf_cmd_factory_mock.go │ │ │ │ ├── list.txt │ │ │ │ ├── requirements.txt │ │ │ │ └── show.txt │ │ ├── pm.go │ │ ├── pm_test.go │ │ ├── sbt │ │ │ ├── README.md │ │ │ ├── build_service.go │ │ │ ├── build_service_test.go │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ │ ├── build_service_mock.go │ │ │ │ ├── cmd_factory_mock.go │ │ │ │ ├── invalidBuild.sbt │ │ │ │ └── notABuild.sbt │ │ ├── testdata │ │ │ └── pm_mock.go │ │ ├── util │ │ │ ├── error.go │ │ │ ├── error_test.go │ │ │ ├── util.go │ │ │ └── util_test.go │ │ ├── writer │ │ │ ├── file_writer.go │ │ │ ├── file_writer_test.go │ │ │ └── testdata │ │ │ │ └── file_writer_mock.go │ │ └── yarn │ │ │ ├── README.md │ │ │ ├── cmd_factory.go │ │ │ ├── cmd_factory_test.go │ │ │ ├── job.go │ │ │ ├── job_test.go │ │ │ ├── pm.go │ │ │ ├── pm_test.go │ │ │ ├── strategy.go │ │ │ ├── strategy_test.go │ │ │ └── testdata │ │ │ └── cmd_factory_mock.go │ ├── resolution.go │ ├── resolution_test.go │ ├── resolver.go │ ├── resolver_test.go │ ├── scheduler.go │ ├── scheduler_test.go │ ├── strategy │ │ ├── strategy.go │ │ ├── strategy_factory.go │ │ ├── strategy_factory_test.go │ │ └── testdata │ │ │ ├── strategy_mock.go │ │ │ └── strategy_mock_factory.go │ └── testdata │ │ └── resolver_mock.go ├── runtime │ └── os │ │ └── os.go ├── scan │ ├── scanner.go │ ├── scanner_test.go │ └── testdata │ │ └── npm │ │ └── package.json ├── tui │ ├── dependency.go │ ├── dependency_test.go │ ├── generation_error_list.go │ ├── generation_error_list_test.go │ ├── progress_bar.go │ ├── progress_bar_test.go │ ├── resolution_error_list.go │ ├── resolution_error_list_test.go │ ├── rule_card.go │ ├── rule_card_test.go │ ├── spinner_manager.go │ ├── spinner_manager_test.go │ └── vulnerability.go ├── upload │ ├── batch.go │ ├── batch_test.go │ ├── result.go │ ├── result_test.go │ ├── status.go │ ├── status_test.go │ ├── testdata │ │ ├── debricked-config-error.yaml │ │ ├── debricked-config.yaml │ │ ├── misc │ │ │ └── requirements.txt │ │ └── yarn │ │ │ ├── package.json │ │ │ └── yarn.lock │ ├── uploader.go │ └── uploader_test.go └── wire │ ├── cli_container.go │ ├── cli_container_test.go │ └── container.go ├── scripts ├── fetch_supported_formats.sh ├── install.sh ├── lint.sh ├── test_cli.sh ├── test_e2e.sh └── test_e2e_callgraph_java_version.sh └── test ├── callgraph ├── maven_test.go └── testdata │ ├── mvnproj-build │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── HelloWorld.java │ └── mvnproj-no-build │ ├── .debrickedTmpFolder │ └── ignore │ ├── pom.xml │ ├── src │ └── main │ │ └── java │ │ └── HelloWorld.java │ └── target │ └── classes │ └── HelloWorld.class ├── checkFilesResolved.sh └── resolve ├── resolver_test.go └── testdata ├── bower └── bower.json ├── composer └── composer.json ├── gomod ├── go.mod └── go.sum ├── gradle └── build.gradle ├── maven └── pom.xml ├── npm └── package.json ├── nuget ├── csproj │ └── basic.csproj └── packagesconfig │ └── packages.config └── pip └── requirements.txt /.github/workflows/debricked.yml: -------------------------------------------------------------------------------- 1 | name: Debricked 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | scan: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-go@v5 16 | with: 17 | go-version: '1.20' 18 | - name: Pull Supported Formats 19 | run: | 20 | cd cmd/debricked 21 | go generate -v -x 22 | - uses: GuillaumeFalourd/assert-command-line-output@v2.3 23 | with: 24 | command_line: go run cmd/debricked/main.go scan -t ${{ secrets.DEBRICKED_TOKEN }} -e "pkg/**" -e "test/**" -e "**/testdata/**" 25 | contains: AUTOMATION RULE 26 | expected_result: PASSED 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Debricked AB. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | sh scripts/install.sh 4 | 5 | .PHONY: lint 6 | lint: 7 | bash scripts/lint.sh 8 | 9 | .PHONY: test 10 | test: 11 | bash scripts/test_cli.sh 12 | 13 | .PHONY: test-e2e 14 | test-e2e: 15 | bash scripts/test_e2e.sh $(type) 16 | 17 | .PHONY: test-e2e-docker 18 | docker-build-dev: 19 | docker build -f build/docker/alpine.Dockerfile -t debricked/cli:dev --target dev . 20 | 21 | .PHONY: docker-build-cli 22 | docker-build-cli: 23 | docker build -f build/docker/alpine.Dockerfile -t debricked/cli:latest --target cli . 24 | 25 | .PHONY: docker-build-scan 26 | docker-build-scan: 27 | docker build -f build/docker/alpine.Dockerfile -t debricked/cli:scan --target scan . 28 | 29 | .PHONY: docker-build-cli-resolution 30 | docker-build-cli-resolution: 31 | docker build -f build/docker/alpine.Dockerfile -t debricked/cli:resolution --target resolution . 32 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Patching 4 | 5 | We release patches for security vulnerabilities. Make sure to stay updated. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Please report (suspected) security vulnerabilities to 10 | **[security@debricked.com](mailto:security@debricked.com)**. You will receive a response from 11 | us within 48 hours. If the issue is confirmed, we will release a patch as soon 12 | as possible. 13 | 14 | This project is included in the Debricked Bug Bounty Program. Please refer to [this website](https://debricked.com/report-vulnerability/) to read more about it. 15 | -------------------------------------------------------------------------------- /UPGRADE-2.0.md: -------------------------------------------------------------------------------- 1 | # Upgrade from 1.X.X to 2.0.0 2 | 3 | ## Changed behaviours 4 | - Changes default strictness of resolve command to 1 (Exit with code 1 if all files failed to resolve, otherwise exit with code 0 instead of always exiting with code 0) 5 | - File Fingerprint analysis is on by default for all repositories which starts with the letters "A-C" (starting from v2.1.4 it is default for all repos). This range will be increased in future minor/patch releases. 6 | - Added inclusion option to commands to force include patterns which are by default ignored by the CLI 7 | - Refactored how exclusion works for fingerprinting to align it with the rest of the CLI, this includes a breaking change for windows where Unix path separators must be used in patterns. 8 | 9 | ## Runtime upgrades 10 | 11 | - Base Docker images have been upgraded from Go 1.21 to 1.22 12 | - In Docker resolution images, the following runtimes have been updated: 13 | - Upgrade Java from OpenJDK 11 to 21 14 | - Upgrade Maven from 3.9.2 to 3.9.6 15 | - Upgrade Gradle from 8.1.1 to 8.7 16 | - Upgrade Node from 18 to 21 17 | - Upgrade dotnet from 7.0 to 8.0 18 | - Upgrade Go from 1.21 to 1.22 19 | - Upgrade PHP from 8.2 to 8.3 20 | - Debian Docker images have been upgraded from Bullseye (11) to Bookworm (12) 21 | -------------------------------------------------------------------------------- /assets/CLI_logo_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/assets/CLI_logo_1024.png -------------------------------------------------------------------------------- /assets/cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/assets/cli.png -------------------------------------------------------------------------------- /assets/debricked_resolve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/assets/debricked_resolve.png -------------------------------------------------------------------------------- /cmd/debricked/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/debricked/cli/internal/cmd/cmderror" 8 | "github.com/debricked/cli/internal/cmd/root" 9 | "github.com/debricked/cli/internal/wire" 10 | ) 11 | 12 | //go:generate sh ../../scripts/fetch_supported_formats.sh 13 | 14 | var version string // Set at compile time 15 | 16 | func main() { 17 | if err := root.NewRootCmd(version, wire.GetCliContainer()).Execute(); err != nil { 18 | var cmdErr cmderror.CommandError 19 | if errors.As(err, &cmdErr) { 20 | os.Exit(cmdErr.Code) 21 | } else { 22 | os.Exit(1) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/templates/Argo/README.md: -------------------------------------------------------------------------------- 1 | # Argo Workflows 2 | - [Default template](argo.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | -------------------------------------------------------------------------------- /examples/templates/Argo/argo.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Workflow 3 | metadata: 4 | generateName: debricked- 5 | spec: 6 | entrypoint: debricked 7 | arguments: 8 | parameters: 9 | - name: git-url # For example: https://github.com/debricked/go-templates.git 10 | - name: debricked-token # Consider using kubernetes secrets instead. For more details, see: https://github.com/argoproj/argo-workflows/blob/master/examples/secrets.yaml 11 | 12 | templates: 13 | - name: debricked 14 | inputs: 15 | parameters: 16 | - name: git-url 17 | - name: debricked-token 18 | artifacts: 19 | - name: repository 20 | path: /repository 21 | git: # For more details, see: https://github.com/argoproj/argo-workflows/blob/master/examples/input-artifact-git.yaml 22 | repo: "{{inputs.parameters.git-url}}" 23 | container: 24 | name: 'debricked-scan' 25 | image: debricked/cli:2-resolution-debian 26 | workingDir: /repository 27 | command: 28 | - debricked scan 29 | env: 30 | - name: DEBRICKED_TOKEN 31 | value: "{{inputs.parameters.debricked-token}}" 32 | - name: DEBRICKED_GIT_URL 33 | value: "{{inputs.parameters.git-url}}" -------------------------------------------------------------------------------- /examples/templates/Azure/README.md: -------------------------------------------------------------------------------- 1 | # Azure Pipelines 2 | - [Default template](azure-pipelines.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | Using Azure Pipelines this can be done by commenting out the authentication step in the template. 12 | More information about this can be found [here](https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/maven-authenticate-v0?view=azure-pipelines). 13 | 14 | ## NuGet 15 | 16 | Just like with maven you need to configure access to private registries or sources, 17 | see [NuGet's documentation on source mapping](https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping) for more information 18 | (and the [authenticated feeds documentation](https://learn.microsoft.com/en-us/nuget/consume-packages/consuming-packages-authenticated-feeds) for details). 19 | When using Azure pipelines your authenticated feeds can be accessed by commenting out the NuGet parts in the default template 20 | (see [nuget authenticate documentation](https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/nuget-authenticate-v1?view=azure-pipelines) for more information). 21 | -------------------------------------------------------------------------------- /examples/templates/Azure/azure-pipelines-private-maven-repository.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: debricked_scan 3 | displayName: Debricked scan 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | steps: 7 | - task: MavenAuthenticate@0 8 | displayName: 'Maven Authenticate' 9 | inputs: 10 | artifactsFeeds: 11 | - script: | 12 | curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 13 | ./debricked scan 14 | displayName: Debricked scan 15 | env: 16 | DEBRICKED_TOKEN: $(DEBRICKED_TOKEN) 17 | -------------------------------------------------------------------------------- /examples/templates/Azure/azure-pipelines-private-nuget-repository.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: debricked_scan 3 | displayName: Debricked scan 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | steps: 7 | - task: NuGetAuthenticate@1 8 | displayName: NuGet Authentication 9 | - script: | 10 | curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 11 | ./debricked scan 12 | displayName: Debricked scan 13 | env: 14 | DEBRICKED_TOKEN: $(DEBRICKED_TOKEN) 15 | -------------------------------------------------------------------------------- /examples/templates/Azure/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: debricked_scan 3 | displayName: Debricked scan 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | steps: 7 | # - task: MavenAuthenticate@0 8 | # displayName: 'Maven Authenticate' 9 | # inputs: 10 | # artifactsFeeds: 11 | # Uncomment the above lines if you need to authenticate private maven registries 12 | # - task: NuGetAuthenticate@1 13 | # displayName: NuGet Authentication 14 | # Uncomment the above lines if you need to authenticate private nuget registries 15 | - script: | 16 | curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 17 | ./debricked scan 18 | displayName: Debricked scan 19 | env: 20 | DEBRICKED_TOKEN: $(DEBRICKED_TOKEN) 21 | -------------------------------------------------------------------------------- /examples/templates/Bitbucket/README.md: -------------------------------------------------------------------------------- 1 | # Bitbucket Pipelines 2 | - [Default template](bitbucket-pipelines.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | -------------------------------------------------------------------------------- /examples/templates/Bitbucket/bitbucket-pipelines.yml: -------------------------------------------------------------------------------- 1 | export: true 2 | 3 | image: 4 | name: atlassian/default-image:2 5 | 6 | definitions: 7 | pipelines: 8 | debricked-scan: &debricked-scan 9 | - step: 10 | name: "Debricked Scan" 11 | script: 12 | - curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 13 | - ./debricked scan 14 | services: 15 | - docker 16 | 17 | pipelines: 18 | default: 19 | - <<: *debricked-scan 20 | -------------------------------------------------------------------------------- /examples/templates/BuildKite/README.md: -------------------------------------------------------------------------------- 1 | # BuildKite 2 | - [Default template](pipeline.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | -------------------------------------------------------------------------------- /examples/templates/BuildKite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - label: ":shield: Debricked" 3 | command: 4 | - curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 5 | - ./debricked scan 6 | -------------------------------------------------------------------------------- /examples/templates/CircleCI/README.md: -------------------------------------------------------------------------------- 1 | # CirleCI 2 | - [Default template](config.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | -------------------------------------------------------------------------------- /examples/templates/CircleCI/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | scan: 5 | docker: 6 | - image: cimg/base:current 7 | steps: 8 | - checkout 9 | - run: curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 10 | - run: ./debricked scan 11 | 12 | workflows: 13 | debricked-scan: 14 | jobs: 15 | - scan 16 | -------------------------------------------------------------------------------- /examples/templates/GitHub/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Actions 2 | - [Default template using Docker image](debricked.yml) 3 | - [Default template without Docker](debricked-non-docker.yml) 4 | 5 | # Private dependencies 6 | 7 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 8 | 9 | ## Maven 10 | 11 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 12 | For more information about maven registries with GitHub Actions see: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry 13 | -------------------------------------------------------------------------------- /examples/templates/GitHub/debricked-non-docker.yml: -------------------------------------------------------------------------------- 1 | name: Debricked scan 2 | 3 | on: [push] 4 | 5 | jobs: 6 | vulnerabilities-scan: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: debricked/actions/cache@v4 12 | - uses: debricked/actions/scan-non-docker@v4 13 | env: 14 | DEBRICKED_TOKEN: ${{ secrets.DEBRICKED_TOKEN }} 15 | -------------------------------------------------------------------------------- /examples/templates/GitHub/debricked.yml: -------------------------------------------------------------------------------- 1 | name: Debricked scan 2 | 3 | on: [push] 4 | 5 | jobs: 6 | vulnerabilities-scan: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: debricked/actions@v4 12 | env: 13 | DEBRICKED_TOKEN: ${{ secrets.DEBRICKED_TOKEN }} 14 | -------------------------------------------------------------------------------- /examples/templates/GitLab/README.md: -------------------------------------------------------------------------------- 1 | # GitLab CI/CD 2 | - [Default template](gitlab-ci.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | For more information about how to set this up for GitLab specifically check out the [documentation](https://docs.gitlab.com/ee/user/packages/maven_repository/). 12 | -------------------------------------------------------------------------------- /examples/templates/GitLab/gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - scan 3 | debricked: 4 | stage: scan 5 | script: 6 | - curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 7 | - ./debricked scan 8 | -------------------------------------------------------------------------------- /examples/templates/Jenkins/README.md: -------------------------------------------------------------------------------- 1 | # Jenkins 2 | - [Default template](Jenkinsfile) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | For more Jenkins specific configuration of the settings file checkout the [documentation](https://plugins.jenkins.io/config-file-provider/). 12 | -------------------------------------------------------------------------------- /examples/templates/Travis/README.md: -------------------------------------------------------------------------------- 1 | # Travis CI 2 | - [Default template](travis.yml) 3 | 4 | # Private dependencies 5 | 6 | Some additional configuration may be required if you use private dependencies not hosted on the default registries, depending on package manager. 7 | 8 | ## Maven 9 | 10 | For maven your `.m2/settings.xml` needs to be configured for the specific registry you wish to use, see the [settings documentation](https://maven.apache.org/settings.html) for more details. 11 | -------------------------------------------------------------------------------- /examples/templates/Travis/travis.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | include: 3 | - stage: Debricked-scan 4 | on: 5 | branch: "*" 6 | env: 7 | - DEBRICKED_TOKEN=${DEBRICKED_TOKEN} 8 | before_install: curl -LsS https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked 9 | before_cache: 10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 12 | cache: 13 | directories: 14 | - $HOME/.cache/go-build 15 | - $HOME/gopath/pkg/mod 16 | - $HOME/.gradle/caches/ 17 | - $HOME/.gradle/wrapper/ 18 | - $HOME/.m2 19 | - $HOME/.cache/pip 20 | script: ./debricked scan 21 | -------------------------------------------------------------------------------- /internal/automation/rule.go: -------------------------------------------------------------------------------- 1 | package automation 2 | 3 | const failPipeline = "failPipeline" 4 | 5 | type Rule struct { 6 | RuleDescription string `json:"ruleDescription"` 7 | RuleActions []string `json:"ruleActions"` 8 | RuleLink string `json:"ruleLink"` 9 | HasCves bool `json:"hasCves"` 10 | Triggered bool `json:"triggered"` 11 | TriggerEvents []TriggerEvent `json:"triggerEvents"` 12 | } 13 | 14 | // FailPipeline checks if rule should fail the pipeline 15 | func (rule *Rule) FailPipeline() bool { 16 | for _, action := range rule.RuleActions { 17 | if action == failPipeline { 18 | return true 19 | } 20 | } 21 | 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /internal/automation/rule_test.go: -------------------------------------------------------------------------------- 1 | package automation 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestFailPipeline(t *testing.T) { 10 | rule := Rule{ 11 | RuleDescription: "Rule description", 12 | RuleActions: []string{"warnPipeline", "email"}, 13 | RuleLink: "link", 14 | HasCves: false, 15 | Triggered: true, 16 | TriggerEvents: nil, 17 | } 18 | assert.False(t, rule.FailPipeline(), "failed to assert that rule passed") 19 | 20 | rule.RuleActions = append(rule.RuleActions, "failPipeline") 21 | 22 | assert.True(t, rule.FailPipeline(), "failed to assert that rule failed") 23 | } 24 | 25 | func TestFailPipelineUnTriggered(t *testing.T) { 26 | rule := Rule{ 27 | RuleDescription: "Rule description", 28 | RuleActions: []string{"warnPipeline", "email", "failPipeline"}, 29 | RuleLink: "link", 30 | HasCves: false, 31 | Triggered: false, 32 | TriggerEvents: nil, 33 | } 34 | assert.True(t, rule.FailPipeline(), "failed to assert that rule failed") 35 | } 36 | -------------------------------------------------------------------------------- /internal/automation/trigger.go: -------------------------------------------------------------------------------- 1 | package automation 2 | 3 | type TriggerEvent struct { 4 | Dependency string `json:"dependency"` 5 | DependencyLink string `json:"dependencyLink"` 6 | Licenses []string `json:"licenses"` 7 | Cve string `json:"cve"` 8 | Cvss2 float32 `json:"cvss2"` 9 | Cvss3 float32 `json:"cvss3"` 10 | CveLink string `json:"cveLink"` 11 | } 12 | -------------------------------------------------------------------------------- /internal/callgraph/README.md: -------------------------------------------------------------------------------- 1 | # Callgraph Generation 2 | 3 | The Debricked CLI can generate static callgraphs for projects to enable reachability analysis for vulnerabilities. 4 | 5 | For the only currently supported language, Java, some setup is required for callgraph generation to work 6 | properly. For more information on this see the Language Support section below. 7 | 8 | ## Language Support 9 | Debricked CLI callgraph generation currently only supports Java, the specific 10 | documentation for the Java callgraph generation can be 11 | found [here](https://github.com/debricked/cli/blob/main/internal/callgraph/language/java11/README.md). 12 | 13 | ## Use 14 | 15 | To generate a callgraph for your project you can use the direct command: 16 | 17 | 18 | ```shell 19 | debricked callgraph 20 | ``` 21 | 22 | ```shell 23 | debricked callgraph --help 24 | ``` 25 | 26 | Or you can enable it in your scan to add reachability analysis: 27 | 28 | ```shell 29 | debricked scan --callgraph 30 | ``` 31 | 32 | To analyze the generated callgraph it needs to be uploaded using the scan command, either with the 33 | callgraph generation flag as above, or with an already generated call graph by omitting the flag. 34 | 35 | For more information see documentation on the specific langauge implementation or see full CLI documentation [here](https://docs.debricked.com/tools-and-integrations/cli/debricked-cli) 36 | -------------------------------------------------------------------------------- /internal/callgraph/cgexec/context.go: -------------------------------------------------------------------------------- 1 | package cgexec 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | type IContext interface { 9 | Context() context.Context 10 | Done() <-chan struct{} 11 | } 12 | 13 | type Context struct { 14 | ctx context.Context 15 | } 16 | 17 | func NewContext(timer int) (Context, context.CancelFunc) { 18 | ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timer)*time.Second) 19 | 20 | return Context{ctx}, cancel 21 | } 22 | 23 | func (c Context) Context() context.Context { 24 | return c.ctx 25 | } 26 | 27 | func (c Context) Done() <-chan struct{} { 28 | return c.ctx.Done() 29 | } 30 | -------------------------------------------------------------------------------- /internal/callgraph/cgexec/context_test.go: -------------------------------------------------------------------------------- 1 | package cgexec 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDone(t *testing.T) { 10 | 11 | ctx, cancel := NewContext(0) 12 | defer cancel() 13 | val := <-ctx.Done() 14 | assert.NotNil(t, val) 15 | } 16 | -------------------------------------------------------------------------------- /internal/callgraph/cgexec/testdata/context_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | type ContextMock struct { 9 | ctx context.Context 10 | } 11 | 12 | func NewContextMock() (ContextMock, context.CancelFunc) { 13 | ctx := context.Background() 14 | return ContextMock{ctx}, nil 15 | } 16 | 17 | func NewContextMockCancelled() (ContextMock, context.CancelFunc) { 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | defer cancel() 20 | return ContextMock{ctx}, nil 21 | } 22 | 23 | func NewContextMockDeadlineReached() (ContextMock, context.CancelFunc) { 24 | ctx := context.Background() 25 | ctx, _ = context.WithDeadline(ctx, time.Now()) 26 | return ContextMock{ctx}, nil 27 | } 28 | 29 | func (c ContextMock) Context() context.Context { 30 | return c.ctx 31 | } 32 | 33 | func (c ContextMock) Done() <-chan struct{} { 34 | context.WithCancel(context.Background()) 35 | return c.ctx.Done() 36 | } 37 | 38 | func (c ContextMock) Err() error { 39 | return context.DeadlineExceeded 40 | } 41 | -------------------------------------------------------------------------------- /internal/callgraph/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type IConfig interface { 4 | Language() string 5 | Args() []string 6 | Kwargs() map[string]string 7 | Build() bool 8 | PackageManager() string 9 | Version() string 10 | } 11 | 12 | type Config struct { 13 | language string 14 | args []string 15 | kwargs map[string]string 16 | build bool 17 | packageManager string 18 | version string 19 | } 20 | 21 | func NewConfig( 22 | language string, 23 | args []string, 24 | kwargs map[string]string, 25 | build bool, 26 | packageManager string, 27 | version string, 28 | ) Config { 29 | return Config{ 30 | language, 31 | args, 32 | kwargs, 33 | build, 34 | packageManager, 35 | version, 36 | } 37 | } 38 | 39 | func (c Config) Language() string { 40 | return c.language 41 | } 42 | 43 | func (c Config) Args() []string { 44 | return c.args 45 | } 46 | 47 | func (c Config) Kwargs() map[string]string { 48 | return c.kwargs 49 | } 50 | 51 | func (c Config) Build() bool { 52 | return c.build 53 | } 54 | 55 | func (c Config) PackageManager() string { 56 | return c.packageManager 57 | } 58 | 59 | func (c Config) Version() string { 60 | return c.version 61 | } 62 | -------------------------------------------------------------------------------- /internal/callgraph/finder/finder.go: -------------------------------------------------------------------------------- 1 | package finder 2 | 3 | type IFinder interface { 4 | FindRoots(files []string) ([]string, error) 5 | FindDependencyDirs(files []string, findJars bool) ([]string, error) 6 | FindFiles(paths []string, exclusions []string, inclusions []string) ([]string, error) 7 | } 8 | -------------------------------------------------------------------------------- /internal/callgraph/finder/golangfinder/testdata/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | PrintHelloWorld(5) 5 | } 6 | -------------------------------------------------------------------------------- /internal/callgraph/finder/golangfinder/testdata/extrapackage/extra.go: -------------------------------------------------------------------------------- 1 | package extra 2 | -------------------------------------------------------------------------------- /internal/callgraph/finder/golangfinder/testdata/random_file.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/callgraph/finder/golangfinder/testdata/random_file.py -------------------------------------------------------------------------------- /internal/callgraph/finder/golangfinder/testdata/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func PrintHelloWorld(times int) { 6 | for i := 0; i < times; i++ { 7 | fmt.Println("Hello, World!") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/pomfinder.go: -------------------------------------------------------------------------------- 1 | package javafinder 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/vifraa/gopom" 7 | ) 8 | 9 | type IPomService interface { 10 | GetRootPomFiles(files []string) []string 11 | ParsePomModules(path string) ([]string, error) 12 | } 13 | 14 | type PomService struct{} 15 | 16 | func (p PomService) ParsePomModules(path string) ([]string, error) { 17 | pom, err := gopom.Parse(path) 18 | 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return pom.Modules, nil 24 | } 25 | 26 | func (p PomService) GetRootPomFiles(files []string) []string { 27 | childMap := make(map[string]bool) 28 | var validFiles []string 29 | var roots []string 30 | 31 | for _, filePath := range files { 32 | modules, err := p.ParsePomModules(filePath) 33 | 34 | if err != nil { 35 | continue 36 | } 37 | 38 | validFiles = append(validFiles, filePath) 39 | 40 | if len(modules) == 0 { 41 | continue 42 | } 43 | 44 | for _, module := range modules { 45 | modulePath := filepath.Join(filepath.Dir(filePath), filepath.Dir(module), filepath.Base(module), "pom.xml") 46 | childMap[modulePath] = true 47 | } 48 | } 49 | 50 | for _, file := range validFiles { 51 | if _, ok := childMap[file]; !ok { 52 | roots = append(roots, file) 53 | } 54 | } 55 | 56 | return roots 57 | } 58 | -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/pomfinder_test.go: -------------------------------------------------------------------------------- 1 | package javafinder 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestParsePomModules(t *testing.T) { 11 | p := PomService{} 12 | modules, err := p.ParsePomModules("testdata/pom.xml") 13 | assert.Nil(t, err) 14 | assert.Len(t, modules, 5) 15 | correct := []string{"guava", "guava-bom", "guava-gwt", "guava-testlib", "guava-tests"} 16 | assert.Equal(t, correct, modules) 17 | 18 | modules, err = p.ParsePomModules("testdata/notAPom.xml") 19 | 20 | assert.NotNil(t, err) 21 | assert.Len(t, modules, 0) 22 | } 23 | 24 | func TestGetRootPomFiles(t *testing.T) { 25 | pomParent := filepath.Join("testdata", "pom.xml") 26 | pomFail := filepath.Join("testdata", "notAPom.xml") 27 | pomChild := filepath.Join("testdata", "guava", "pom.xml") 28 | 29 | p := PomService{} 30 | files := p.GetRootPomFiles([]string{pomParent, pomFail}) 31 | assert.Len(t, files, 1) 32 | 33 | files = p.GetRootPomFiles([]string{pomParent, pomChild}) 34 | assert.Len(t, files, 1) 35 | assert.Equal(t, pomParent, files[0]) 36 | 37 | files = p.GetRootPomFiles([]string{pomFail}) 38 | assert.Len(t, files, 0) 39 | } 40 | -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "os/exec" 4 | 5 | type CmdFactoryMock struct { 6 | Err error 7 | Name string 8 | Arg string 9 | } 10 | 11 | func (f CmdFactoryMock) MakeDependencyTreeCmd(_ string) (*exec.Cmd, error) { 12 | if len(f.Arg) == 0 { 13 | f.Arg = `"MakeDependencyTreeCmd"` 14 | } 15 | return exec.Command(f.Name, f.Arg), f.Err 16 | } 17 | -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/testdata/notAPom.xml: -------------------------------------------------------------------------------- 1 | pandas==1.1.1 2 | # comment 3 | numpy==1.2.3 -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/testdata/test_project/excluded_folder/excluded_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/callgraph/finder/javafinder/testdata/test_project/excluded_folder/excluded_file.txt -------------------------------------------------------------------------------- /internal/callgraph/finder/javafinder/testdata/test_project/included_folder/included_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/callgraph/finder/javafinder/testdata/test_project/included_folder/included_file.txt -------------------------------------------------------------------------------- /internal/callgraph/finder/testdata/finder_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type FinderMock struct { 4 | FindDependencyDirsNames []string 5 | FindDependencyDirsErr error 6 | FindRootsNames []string 7 | FindRootsErr error 8 | FindFilesNames []string 9 | FindFilesErr error 10 | } 11 | 12 | func NewEmptyFinderMock() FinderMock { 13 | return FinderMock{ 14 | FindDependencyDirsNames: []string{}, 15 | FindRootsNames: []string{}, 16 | FindFilesNames: []string{}, 17 | } 18 | } 19 | 20 | func (f FinderMock) FindDependencyDirs(_ []string, _ bool) ([]string, error) { 21 | return f.FindDependencyDirsNames, f.FindDependencyDirsErr 22 | } 23 | 24 | func (f FinderMock) FindRoots(_ []string) ([]string, error) { 25 | return f.FindRootsNames, f.FindRootsErr 26 | } 27 | 28 | func (f FinderMock) FindFiles(_ []string, _ []string, _ []string) ([]string, error) { 29 | return f.FindFilesNames, f.FindFilesErr 30 | } 31 | -------------------------------------------------------------------------------- /internal/callgraph/generation.go: -------------------------------------------------------------------------------- 1 | package callgraph 2 | 3 | import "github.com/debricked/cli/internal/callgraph/job" 4 | 5 | type IGeneration interface { 6 | Jobs() []job.IJob 7 | HasErr() bool 8 | } 9 | 10 | type Generation struct { 11 | jobs []job.IJob 12 | } 13 | 14 | func NewGeneration(jobs []job.IJob) Generation { 15 | return Generation{jobs} 16 | } 17 | 18 | func (g Generation) Jobs() []job.IJob { 19 | return g.jobs 20 | } 21 | 22 | func (g Generation) HasErr() bool { 23 | for _, j := range g.Jobs() { 24 | if j.Errors().HasError() { 25 | return true 26 | } 27 | } 28 | 29 | return false 30 | } 31 | -------------------------------------------------------------------------------- /internal/callgraph/job/base_job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "errors" 5 | "os/exec" 6 | 7 | err "github.com/debricked/cli/internal/io/err" 8 | ) 9 | 10 | type BaseJob struct { 11 | dir string 12 | files []string 13 | errs err.IErrors 14 | status chan string 15 | } 16 | 17 | func NewBaseJob(dir string, files []string) BaseJob { 18 | return BaseJob{ 19 | dir: dir, 20 | files: files, 21 | errs: err.NewErrors(dir), 22 | status: make(chan string), 23 | } 24 | } 25 | 26 | func (j *BaseJob) GetDir() string { 27 | return j.dir 28 | } 29 | 30 | func (j *BaseJob) GetFiles() []string { 31 | return j.files 32 | } 33 | 34 | func (j *BaseJob) Errors() err.IErrors { 35 | return j.errs 36 | } 37 | 38 | func (j *BaseJob) ReceiveStatus() chan string { 39 | return j.status 40 | } 41 | 42 | func (j *BaseJob) SendStatus(status string) { 43 | j.status <- status 44 | } 45 | 46 | func (j *BaseJob) GetExitError(err error) error { 47 | exitErr, ok := err.(*exec.ExitError) 48 | if !ok { 49 | return err 50 | } 51 | 52 | return errors.New(string(exitErr.Stderr)) 53 | } 54 | -------------------------------------------------------------------------------- /internal/callgraph/job/job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import error "github.com/debricked/cli/internal/io/err" 4 | 5 | type IJob interface { 6 | GetFiles() []string 7 | GetDir() string 8 | Errors() error.IErrors 9 | Run() 10 | ReceiveStatus() chan string 11 | } 12 | -------------------------------------------------------------------------------- /internal/callgraph/job/testdata/job_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/io/err" 7 | ) 8 | 9 | type JobMock struct { 10 | dir string 11 | files []string 12 | errs err.IErrors 13 | status chan string 14 | } 15 | 16 | func (j *JobMock) ReceiveStatus() chan string { 17 | return j.status 18 | } 19 | 20 | func (j *JobMock) GetDir() string { 21 | return j.dir 22 | } 23 | 24 | func (j *JobMock) GetFiles() []string { 25 | return j.files 26 | } 27 | 28 | func (j *JobMock) Errors() err.IErrors { 29 | return j.errs 30 | } 31 | 32 | func (j *JobMock) Run() { 33 | fmt.Println("job mock run") 34 | } 35 | 36 | func NewJobMock(dir string, files []string) *JobMock { 37 | return &JobMock{ 38 | dir: dir, 39 | files: files, 40 | status: make(chan string), 41 | errs: err.NewErrors(dir), 42 | } 43 | } 44 | 45 | func (j *JobMock) SetErr(err err.IError) { 46 | j.errs.Critical(err) 47 | } 48 | -------------------------------------------------------------------------------- /internal/callgraph/job/testdata/job_test_util.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | 8 | "github.com/debricked/cli/internal/callgraph/job" 9 | "github.com/debricked/cli/internal/io/err" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func AssertPathErr(t *testing.T, jobErrs err.IErrors) { 14 | var path string 15 | if runtime.GOOS == "windows" { 16 | path = "%PATH%" 17 | } else { 18 | path = "$PATH" 19 | } 20 | errs := jobErrs.GetAll() 21 | assert.Len(t, errs, 1) 22 | err := errs[0] 23 | errMsg := fmt.Sprintf("executable file not found in %s", path) 24 | assert.ErrorContains(t, err, errMsg) 25 | } 26 | 27 | func WaitStatus(j job.IJob) { 28 | for { 29 | <-j.ReceiveStatus() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/callgraph/language/golang/README.md: -------------------------------------------------------------------------------- 1 | # TODO: add docs to this readme on how we generate go CGs -------------------------------------------------------------------------------- /internal/callgraph/language/golang/language.go: -------------------------------------------------------------------------------- 1 | package golang 2 | 3 | const Name = "golang" 4 | const StandardVersion = "1" 5 | 6 | type Language struct { 7 | name string 8 | version string 9 | } 10 | 11 | func NewLanguage() Language { 12 | return Language{ 13 | name: Name, 14 | version: StandardVersion, 15 | } 16 | } 17 | 18 | func (language Language) Name() string { 19 | return language.name 20 | } 21 | 22 | func (language Language) Version() string { 23 | return language.version 24 | } 25 | -------------------------------------------------------------------------------- /internal/callgraph/language/golang/language_test.go: -------------------------------------------------------------------------------- 1 | package golang 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewLanguage(t *testing.T) { 10 | pm := NewLanguage() 11 | assert.Equal(t, Name, pm.name) 12 | assert.Equal(t, StandardVersion, pm.version) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewLanguage() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestVersion(t *testing.T) { 21 | pm := NewLanguage() 22 | assert.Equal(t, StandardVersion, pm.Version()) 23 | } 24 | -------------------------------------------------------------------------------- /internal/callgraph/language/golang/testdata/callgraph_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type ICallgraph interface { 4 | RunCallGraph() (string, error) 5 | } 6 | 7 | type CallgraphMock struct { 8 | RunCallGraphOutput string 9 | RunCallGraphError error 10 | } 11 | 12 | func (cm CallgraphMock) RunCallGraph() (string, error) { 13 | return cm.RunCallGraphOutput, cm.RunCallGraphError 14 | 15 | } 16 | -------------------------------------------------------------------------------- /internal/callgraph/language/golang/testdata/fixture/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func sayHello() string { 4 | return "Hello, World!" 5 | } 6 | 7 | func main() { 8 | sayHello() 9 | } 10 | -------------------------------------------------------------------------------- /internal/callgraph/language/java/embedded/SootWrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/callgraph/language/java/embedded/SootWrapper.jar -------------------------------------------------------------------------------- /internal/callgraph/language/java/embedded/gradle-script.groovy: -------------------------------------------------------------------------------- 1 | 2 | 3 | allprojects{ 4 | task debrickedCopyDependencies(type: Copy) { 5 | into ".debrickedTmpDir" 6 | from configurations.default 7 | } 8 | } -------------------------------------------------------------------------------- /internal/callgraph/language/java/language.go: -------------------------------------------------------------------------------- 1 | package java 2 | 3 | const Name = "java" 4 | const StandardVersion = "11" 5 | 6 | type Language struct { 7 | name string 8 | version string 9 | } 10 | 11 | func NewLanguage() Language { 12 | return Language{ 13 | name: Name, 14 | version: StandardVersion, 15 | } 16 | } 17 | 18 | func (language Language) Name() string { 19 | return language.name 20 | } 21 | 22 | func (language Language) Version() string { 23 | return language.version 24 | } 25 | -------------------------------------------------------------------------------- /internal/callgraph/language/java/language_test.go: -------------------------------------------------------------------------------- 1 | package java 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewLanguage(t *testing.T) { 10 | pm := NewLanguage() 11 | assert.Equal(t, Name, pm.name) 12 | assert.Equal(t, StandardVersion, pm.version) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewLanguage() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestVersion(t *testing.T) { 21 | pm := NewLanguage() 22 | assert.Equal(t, StandardVersion, pm.Version()) 23 | } 24 | -------------------------------------------------------------------------------- /internal/callgraph/language/java/soot-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/callgraph/language/java/soot-wrapper.jar -------------------------------------------------------------------------------- /internal/callgraph/language/java/testdata/callgraph_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type ICallgraph interface { 4 | RunCallGraphWithSetup() error 5 | RunCallGraph(callgraphJarPath string) error 6 | } 7 | 8 | type CallgraphMock struct { 9 | RunCallGraphWithSetupError error 10 | RunCallGraphError error 11 | } 12 | 13 | func (cm CallgraphMock) RunCallGraphWithSetup() error { 14 | return cm.RunCallGraphWithSetupError 15 | } 16 | 17 | func (cm CallgraphMock) RunCallGraph(callgraphJarPath string) error { 18 | return cm.RunCallGraphError 19 | 20 | } 21 | -------------------------------------------------------------------------------- /internal/callgraph/language/language.go: -------------------------------------------------------------------------------- 1 | package language 2 | 3 | import "github.com/debricked/cli/internal/callgraph/language/java" 4 | 5 | type ILanguage interface { 6 | Name() string 7 | Version() string 8 | } 9 | 10 | func Languages() []ILanguage { 11 | return []ILanguage{ 12 | java.NewLanguage(), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/callgraph/language/language_test.go: -------------------------------------------------------------------------------- 1 | package language 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestLanguages(t *testing.T) { 10 | langs := Languages() 11 | langNames := []string{ 12 | "java", 13 | } 14 | 15 | for _, langName := range langNames { 16 | t.Run(langName, func(t *testing.T) { 17 | contains := false 18 | for _, pm := range langs { 19 | contains = contains || pm.Name() == langName 20 | } 21 | assert.Truef(t, contains, "failed to assert that %s was returned in Languages()", langName) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/callgraph/strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/callgraph/job" 5 | ) 6 | 7 | type IStrategy interface { 8 | Invoke() ([]job.IJob, error) 9 | } 10 | -------------------------------------------------------------------------------- /internal/callgraph/strategy/strategy_factory.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/callgraph/cgexec" 7 | conf "github.com/debricked/cli/internal/callgraph/config" 8 | golangfinder "github.com/debricked/cli/internal/callgraph/finder/golangfinder" 9 | "github.com/debricked/cli/internal/callgraph/finder/javafinder" 10 | "github.com/debricked/cli/internal/callgraph/language/golang" 11 | "github.com/debricked/cli/internal/callgraph/language/java" 12 | ) 13 | 14 | type IFactory interface { 15 | Make(config conf.IConfig, paths []string, exclusions []string, inclusions []string, ctx cgexec.IContext) (IStrategy, error) 16 | } 17 | 18 | type Factory struct{} 19 | 20 | func NewStrategyFactory() Factory { 21 | return Factory{} 22 | } 23 | 24 | func (sf Factory) Make( 25 | config conf.IConfig, 26 | paths []string, 27 | exclusions []string, 28 | inclusions []string, 29 | ctx cgexec.IContext, 30 | ) (IStrategy, error) { 31 | name := config.Language() 32 | switch name { 33 | case java.Name: 34 | return java.NewStrategy(config, paths, exclusions, inclusions, javafinder.JavaFinder{}, ctx), nil 35 | case golang.Name: 36 | return golang.NewStrategy(config, paths, exclusions, inclusions, golangfinder.GolangFinder{}, ctx), nil 37 | default: 38 | return nil, fmt.Errorf("failed to make strategy from %s", name) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/callgraph/strategy/strategy_factory_test.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/callgraph/config" 7 | "github.com/debricked/cli/internal/callgraph/finder/javafinder" 8 | "github.com/debricked/cli/internal/callgraph/language/java" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestNewStrategyFactory(t *testing.T) { 13 | f := NewStrategyFactory() 14 | assert.NotNil(t, f) 15 | } 16 | 17 | func TestMakeErr(t *testing.T) { 18 | f := NewStrategyFactory() 19 | conf := config.NewConfig("test", nil, nil, true, "", "") 20 | s, err := f.Make(conf, nil, nil, nil, nil) 21 | assert.Nil(t, s) 22 | assert.ErrorContains(t, err, "failed to make strategy from test") 23 | } 24 | 25 | func TestMake(t *testing.T) { 26 | conf := config.NewConfig(java.Name, nil, nil, true, "", "") 27 | cases := map[string]IStrategy{ 28 | java.Name: java.NewStrategy(conf, []string{}, []string{}, []string{}, javafinder.JavaFinder{}, nil), 29 | } 30 | f := NewStrategyFactory() 31 | for name, strategy := range cases { 32 | t.Run(name, func(t *testing.T) { 33 | s, err := f.Make(conf, []string{}, []string{}, []string{}, nil) 34 | assert.NoError(t, err) 35 | assert.Equal(t, strategy, s) 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/callgraph/strategy/testdata/strategy_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/debricked/cli/internal/callgraph/cgexec" 7 | "github.com/debricked/cli/internal/callgraph/config" 8 | "github.com/debricked/cli/internal/callgraph/finder" 9 | "github.com/debricked/cli/internal/callgraph/job" 10 | "github.com/debricked/cli/internal/callgraph/job/testdata" 11 | ) 12 | 13 | type StrategyMock struct { 14 | config config.IConfig 15 | files []string 16 | finder finder.IFinder 17 | ctx cgexec.IContext 18 | } 19 | 20 | func NewStrategyMock(config config.IConfig, files []string, finder finder.IFinder, ctx cgexec.IContext) StrategyMock { 21 | return StrategyMock{config, files, finder, ctx} 22 | } 23 | 24 | func (s StrategyMock) Invoke() ([]job.IJob, error) { 25 | var jobs []job.IJob 26 | jobs = append(jobs, testdata.NewJobMock("dir", s.files)) 27 | 28 | return jobs, nil 29 | } 30 | 31 | type StrategyErrorMock struct { 32 | config config.IConfig 33 | files []string 34 | finder finder.IFinder 35 | ctx cgexec.IContext 36 | } 37 | 38 | func NewStrategyErrorMock(config config.IConfig, files []string, finder finder.IFinder, ctx cgexec.IContext) StrategyErrorMock { 39 | return StrategyErrorMock{config, files, finder, ctx} 40 | } 41 | 42 | func (s StrategyErrorMock) Invoke() ([]job.IJob, error) { 43 | 44 | return nil, errors.New("mock-error") 45 | } 46 | -------------------------------------------------------------------------------- /internal/callgraph/strategy/testdata/strategy_mock_factory.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/callgraph/cgexec" 5 | "github.com/debricked/cli/internal/callgraph/config" 6 | "github.com/debricked/cli/internal/callgraph/finder/testdata" 7 | "github.com/debricked/cli/internal/callgraph/strategy" 8 | ) 9 | 10 | type FactoryMock struct{} 11 | 12 | func NewStrategyFactoryMock() FactoryMock { 13 | return FactoryMock{} 14 | } 15 | 16 | func (sf FactoryMock) Make( 17 | config config.IConfig, 18 | paths []string, 19 | exclusions []string, 20 | inclusions []string, 21 | ctx cgexec.IContext, 22 | ) (strategy.IStrategy, error) { 23 | return NewStrategyMock(config, paths, testdata.FinderMock{}, ctx), nil 24 | } 25 | 26 | type FactoryErrorMock struct{} 27 | 28 | func NewStrategyFactoryErrorMock() FactoryErrorMock { 29 | return FactoryErrorMock{} 30 | } 31 | 32 | func (sf FactoryErrorMock) Make( 33 | config config.IConfig, 34 | paths []string, 35 | exclusions []string, 36 | inclusions []string, 37 | ctx cgexec.IContext, 38 | ) (strategy.IStrategy, error) { 39 | return NewStrategyErrorMock(config, paths, testdata.FinderMock{}, ctx), nil 40 | } 41 | -------------------------------------------------------------------------------- /internal/callgraph/testdata/generator_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/debricked/cli/internal/callgraph" 8 | "github.com/debricked/cli/internal/callgraph/cgexec" 9 | ) 10 | 11 | type GeneratorMock struct { 12 | Err error 13 | files []string 14 | } 15 | 16 | func (r *GeneratorMock) GenerateWithTimer(_ callgraph.DebrickedOptions) error { 17 | return r.Err 18 | } 19 | 20 | func (r *GeneratorMock) Generate(_ callgraph.DebrickedOptions, _ cgexec.IContext) error { 21 | for _, f := range r.files { 22 | createdFile, err := os.Create(f) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | err = createdFile.Close() 28 | if err != nil { 29 | return err 30 | } 31 | } 32 | 33 | return r.Err 34 | } 35 | 36 | func (r *GeneratorMock) SetFiles(files []string) { 37 | r.files = files 38 | } 39 | 40 | func (r *GeneratorMock) CleanUp() error { 41 | for _, f := range r.files { 42 | abs, err := filepath.Abs(f) 43 | if err != nil { 44 | return err 45 | } 46 | err = os.Remove(abs) 47 | if err != nil { 48 | return err 49 | } 50 | } 51 | 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /internal/ci/azure/ci.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/debricked/cli/internal/ci/env" 9 | "github.com/debricked/cli/internal/ci/util" 10 | ) 11 | 12 | const ( 13 | EnvKey = "TF_BUILD" 14 | Integration = "azureDevOps" 15 | ) 16 | 17 | type Ci struct{} 18 | 19 | func (_ Ci) Identify() bool { 20 | return util.EnvKeyIsSet(EnvKey) 21 | } 22 | 23 | func (_ Ci) Map() (env.Env, error) { 24 | e := env.Env{} 25 | owner := filepath.Base(os.Getenv("SYSTEM_COLLECTIONURI")) 26 | e.Repository = fmt.Sprintf("%s/%s", owner, os.Getenv("BUILD_REPOSITORY_NAME")) 27 | e.Commit = os.Getenv("BUILD_SOURCEVERSION") 28 | e.Branch = os.Getenv("BUILD_SOURCEBRANCHNAME") 29 | e.RepositoryUrl = os.Getenv("BUILD_REPOSITORY_URI") 30 | e.Integration = Integration 31 | e.Author = os.Getenv("BUILD_REQUESTEDFOREMAIL") 32 | e.Filepath = os.Getenv("BUILD_SOURCESDIRECTORY") 33 | 34 | return e, nil 35 | } 36 | -------------------------------------------------------------------------------- /internal/ci/azure/ci_test.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/ci/testdata" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var azureEnv = map[string]string{ 11 | "TF_BUILD": "azure", 12 | "SYSTEM_COLLECTIONURI": "dir/debricked/", 13 | "BUILD_REPOSITORY_NAME": "cli", 14 | "BUILD_SOURCEVERSION": "commit", 15 | "BUILD_SOURCEBRANCHNAME": "main", 16 | "BUILD_SOURCESDIRECTORY": ".", 17 | "BUILD_REPOSITORY_URI": "https://github.com/debricked/cli", 18 | "BUILD_REQUESTEDFOREMAIL": "viktigpetterr ", 19 | } 20 | 21 | func TestIdentify(t *testing.T) { 22 | testdata.AssertIdentify(t, Ci{}.Identify, EnvKey) 23 | } 24 | 25 | func TestParse(t *testing.T) { 26 | testdata.SetUpCiEnv(t, azureEnv) 27 | defer testdata.ResetEnv(t, azureEnv) 28 | ci := Ci{} 29 | 30 | env, _ := ci.Map() 31 | 32 | assert.Equal(t, azureEnv["BUILD_SOURCESDIRECTORY"], env.Filepath) 33 | assert.Equal(t, Integration, env.Integration) 34 | assert.Equal(t, azureEnv["BUILD_REQUESTEDFOREMAIL"], env.Author) 35 | assert.Equal(t, azureEnv["BUILD_SOURCEBRANCHNAME"], env.Branch) 36 | assert.Equal(t, azureEnv["BUILD_REPOSITORY_URI"], env.RepositoryUrl) 37 | assert.Equal(t, azureEnv["BUILD_SOURCEVERSION"], env.Commit) 38 | assert.Equal(t, "debricked/cli", env.Repository) 39 | 40 | } 41 | -------------------------------------------------------------------------------- /internal/ci/bitbucket/ci.go: -------------------------------------------------------------------------------- 1 | package bitbucket 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/debricked/cli/internal/ci/env" 8 | "github.com/debricked/cli/internal/ci/util" 9 | "github.com/debricked/cli/internal/git" 10 | ) 11 | 12 | const ( 13 | EnvKey = "BITBUCKET_BUILD_NUMBER" 14 | Integration = "bitbucket" 15 | ) 16 | 17 | type Ci struct{} 18 | 19 | func (_ Ci) Identify() bool { 20 | return util.EnvKeyIsSet(EnvKey) 21 | } 22 | 23 | func (_ Ci) Map() (env.Env, error) { 24 | e := env.Env{} 25 | e.Repository = fmt.Sprintf("%s/%s", os.Getenv("BITBUCKET_REPO_OWNER"), os.Getenv("BITBUCKET_REPO_SLUG")) 26 | e.Commit = os.Getenv("BITBUCKET_COMMIT") 27 | e.Branch = os.Getenv("BITBUCKET_BRANCH") 28 | e.RepositoryUrl = os.Getenv("BITBUCKET_GIT_HTTP_ORIGIN") 29 | e.Integration = Integration 30 | repo, err := git.FindRepository(e.Filepath) 31 | if err != nil { 32 | return e, err 33 | } 34 | author, err := git.FindCommitAuthor(repo) 35 | e.Author = author 36 | 37 | return e, err 38 | } 39 | -------------------------------------------------------------------------------- /internal/ci/ci.go: -------------------------------------------------------------------------------- 1 | package ci 2 | 3 | import "github.com/debricked/cli/internal/ci/env" 4 | 5 | type ICi interface { 6 | Identify() bool 7 | Map() (env.Env, error) 8 | } 9 | -------------------------------------------------------------------------------- /internal/ci/env/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | type Env struct { 4 | Repository string 5 | Commit string 6 | Branch string 7 | Author string 8 | RepositoryUrl string 9 | Integration string 10 | Filepath string 11 | } 12 | -------------------------------------------------------------------------------- /internal/ci/github/ci.go: -------------------------------------------------------------------------------- 1 | package github 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/debricked/cli/internal/ci/env" 9 | "github.com/debricked/cli/internal/ci/util" 10 | ) 11 | 12 | const ( 13 | EnvKey = "GITHUB_ACTION" 14 | Integration = "githubActions" 15 | ) 16 | 17 | type Ci struct{} 18 | 19 | func (_ Ci) Identify() bool { 20 | return util.EnvKeyIsSet(EnvKey) 21 | } 22 | 23 | func (_ Ci) Map() (env.Env, error) { 24 | e := env.Env{} 25 | e.Repository = os.Getenv("GITHUB_REPOSITORY") 26 | e.Commit = os.Getenv("GITHUB_SHA") 27 | 28 | // GitHub gives branches as: refs/heads/master, and tags as refs/tags/v1.1.0. 29 | // Remove prefix refs/{tags,heads} from the name before sending to Debricked. 30 | gitHubRef := os.Getenv("GITHUB_REF") 31 | branch := strings.Replace(gitHubRef, "refs/heads/", "", 1) 32 | branch = strings.Replace(branch, "refs/tags/", "", 1) 33 | if strings.Contains(branch, "/merge") { 34 | branch = os.Getenv("GITHUB_HEAD_REF") 35 | } 36 | e.Branch = branch 37 | 38 | e.RepositoryUrl = fmt.Sprintf("https://github.com/%s", os.Getenv("GITHUB_REPOSITORY")) 39 | e.Integration = Integration 40 | e.Author = os.Getenv("GITHUB_ACTOR") 41 | 42 | return e, nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/ci/gitlab/ci.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/debricked/cli/internal/ci/env" 7 | "github.com/debricked/cli/internal/ci/util" 8 | ) 9 | 10 | const ( 11 | EnvKey = "GITLAB_CI" 12 | Integration = "gitlab" 13 | ) 14 | 15 | type Ci struct{} 16 | 17 | func (_ Ci) Identify() bool { 18 | return util.EnvKeyIsSet(EnvKey) 19 | } 20 | 21 | func (_ Ci) Map() (env.Env, error) { 22 | e := env.Env{} 23 | e.Repository = os.Getenv("CI_PROJECT_PATH") 24 | e.Commit = os.Getenv("CI_COMMIT_SHA") 25 | e.Branch = os.Getenv("CI_COMMIT_REF_NAME") 26 | e.RepositoryUrl = os.Getenv("CI_PROJECT_URL") 27 | e.Integration = Integration 28 | e.Filepath = os.Getenv("CI_PROJECT_DIR") 29 | e.Author = os.Getenv("CI_COMMIT_AUTHOR") 30 | 31 | return e, nil 32 | } 33 | -------------------------------------------------------------------------------- /internal/ci/gitlab/ci_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/ci/testdata" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var gitLabEnv = map[string]string{ 11 | "GITLAB_CI": "gitlab", 12 | "CI_PROJECT_PATH": "debricked/cli", 13 | "CI_COMMIT_SHA": "commit", 14 | "CI_COMMIT_REF_NAME": "main", 15 | "CI_PROJECT_DIR": "/", 16 | "CI_PROJECT_URL": "https://gitlab.com/debricked/cli", 17 | "CI_COMMIT_AUTHOR": "viktigpetterr ", 18 | } 19 | 20 | func TestIdentify(t *testing.T) { 21 | testdata.AssertIdentify(t, Ci{}.Identify, EnvKey) 22 | } 23 | 24 | func TestParse(t *testing.T) { 25 | testdata.SetUpCiEnv(t, gitLabEnv) 26 | defer testdata.ResetEnv(t, gitLabEnv) 27 | ci := Ci{} 28 | 29 | env, _ := ci.Map() 30 | 31 | assert.Equal(t, gitLabEnv["CI_PROJECT_DIR"], env.Filepath) 32 | assert.Equal(t, Integration, env.Integration) 33 | assert.Equal(t, gitLabEnv["CI_COMMIT_AUTHOR"], env.Author) 34 | assert.Equal(t, gitLabEnv["CI_COMMIT_REF_NAME"], env.Branch) 35 | assert.Equal(t, "https://gitlab.com/debricked/cli", env.RepositoryUrl) 36 | assert.Equal(t, gitLabEnv["CI_COMMIT_SHA"], env.Commit) 37 | assert.Equal(t, "debricked/cli", env.Repository) 38 | } 39 | -------------------------------------------------------------------------------- /internal/ci/service.go: -------------------------------------------------------------------------------- 1 | package ci 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/debricked/cli/internal/ci/argo" 8 | "github.com/debricked/cli/internal/ci/azure" 9 | "github.com/debricked/cli/internal/ci/bitbucket" 10 | "github.com/debricked/cli/internal/ci/buildkite" 11 | "github.com/debricked/cli/internal/ci/circleci" 12 | "github.com/debricked/cli/internal/ci/env" 13 | "github.com/debricked/cli/internal/ci/github" 14 | "github.com/debricked/cli/internal/ci/gitlab" 15 | "github.com/debricked/cli/internal/ci/travis" 16 | ) 17 | 18 | type IService interface { 19 | Find() (env.Env, error) 20 | } 21 | 22 | var ErrNotSupported = errors.New("CI is not supported") 23 | 24 | type Service struct { 25 | cis []ICi 26 | } 27 | 28 | func NewService(cis []ICi) *Service { 29 | if cis == nil { 30 | return &Service{ 31 | []ICi{ 32 | argo.Ci{}, 33 | azure.Ci{}, 34 | bitbucket.Ci{}, 35 | buildkite.Ci{}, 36 | circleci.Ci{}, 37 | github.Ci{}, 38 | gitlab.Ci{}, 39 | travis.Ci{}, 40 | }, 41 | } 42 | } 43 | 44 | return &Service{cis} 45 | } 46 | 47 | func (s *Service) Find() (env.Env, error) { 48 | for _, ci := range s.cis { 49 | if ci.Identify() { 50 | m, err := ci.Map() 51 | fmt.Println("Integration:", m.Integration) 52 | 53 | return m, err 54 | } 55 | } 56 | 57 | return env.Env{}, ErrNotSupported 58 | } 59 | -------------------------------------------------------------------------------- /internal/ci/service_test.go: -------------------------------------------------------------------------------- 1 | package ci 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/debricked/cli/internal/ci/azure" 8 | "github.com/debricked/cli/internal/ci/circleci" 9 | "github.com/debricked/cli/internal/ci/gitlab" 10 | "github.com/debricked/cli/internal/ci/testdata" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestNewService(t *testing.T) { 15 | s := NewService([]ICi{}) 16 | assert.Empty(t, s.cis) 17 | 18 | s = NewService(nil) 19 | assert.Len(t, s.cis, 8) 20 | 21 | s.cis = []ICi{gitlab.Ci{}} 22 | assert.Len(t, s.cis, 1) 23 | 24 | _, ok := s.cis[0].(gitlab.Ci) 25 | assert.True(t, ok, "failed to assert that the CI was gitlab.Ci") 26 | } 27 | 28 | func TestFindNotSupported(t *testing.T) { 29 | s := NewService([]ICi{azure.Ci{}, circleci.Ci{}}) 30 | _, err := s.Find() 31 | assert.ErrorIs(t, err, ErrNotSupported) 32 | } 33 | 34 | func TestFind(t *testing.T) { 35 | _ = os.Setenv(azure.EnvKey, "value") 36 | defer testdata.UnsetEnvVar(t, azure.EnvKey) 37 | 38 | s := NewService([]ICi{azure.Ci{}, circleci.Ci{}}) 39 | env, err := s.Find() 40 | assert.NoError(t, err) 41 | assert.Greater(t, len(env.Integration), 0) 42 | } 43 | -------------------------------------------------------------------------------- /internal/ci/testdata/ci_assert.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func AssertIdentify(t *testing.T, identify func() bool, envKey string) { 11 | assert.False(t, identify(), "failed to assert that CI was not identified") 12 | 13 | _ = os.Setenv(envKey, "value") 14 | defer UnsetEnvVar(t, envKey) 15 | 16 | assert.True(t, identify(), "failed to assert that CI was identified") 17 | } 18 | -------------------------------------------------------------------------------- /internal/ci/travis/ci.go: -------------------------------------------------------------------------------- 1 | package travis 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/debricked/cli/internal/ci/env" 8 | "github.com/debricked/cli/internal/ci/util" 9 | "github.com/debricked/cli/internal/git" 10 | ) 11 | 12 | const ( 13 | EnvKey = "TRAVIS" 14 | Integration = "travis" 15 | ) 16 | 17 | type Ci struct{} 18 | 19 | func (_ Ci) Identify() bool { 20 | return util.EnvKeyIsSet(EnvKey) 21 | } 22 | 23 | func (_ Ci) Map() (env.Env, error) { 24 | e := env.Env{} 25 | e.Repository = os.Getenv("TRAVIS_REPO_SLUG") 26 | e.Commit = os.Getenv("TRAVIS_COMMIT") 27 | e.Branch = os.Getenv("TRAVIS_BRANCH") 28 | e.RepositoryUrl = fmt.Sprintf("https://github.com/%s", e.Repository) 29 | e.Integration = Integration 30 | //# The absolute path to the directory where the repository being built has been copied on the worker. 31 | //# HOME is set to /home/travis on Linux, /Users/travis on MacOS, and /c/Users/travis on Windows. 32 | e.Filepath = os.Getenv("TRAVIS_BUILD_DIR") 33 | repo, err := git.FindRepository(e.Filepath) 34 | if err != nil { 35 | return e, err 36 | } 37 | author, err := git.FindCommitAuthor(repo) 38 | e.Author = author 39 | 40 | return e, err 41 | } 42 | -------------------------------------------------------------------------------- /internal/ci/travis/ci_test.go: -------------------------------------------------------------------------------- 1 | package travis 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/ci/env" 7 | "github.com/debricked/cli/internal/ci/testdata" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var travisEnv = map[string]string{ 12 | "TRAVIS_REPO_SLUG": "debricked/cli", 13 | "TRAVIS_BRANCH": "main", 14 | "TRAVIS_COMMIT": "commit", 15 | "TRAVIS_BUILD_DIR": ".", 16 | } 17 | 18 | func TestIdentify(t *testing.T) { 19 | testdata.AssertIdentify(t, Ci{}.Identify, EnvKey) 20 | } 21 | 22 | func TestParse(t *testing.T) { 23 | testdata.SetUpCiEnv(t, travisEnv) 24 | defer testdata.ResetEnv(t, travisEnv) 25 | 26 | cwd := testdata.SetUpGitRepository(t, true) 27 | defer testdata.TearDownGitRepository(cwd, t) 28 | 29 | ci := Ci{} 30 | e, err := ci.Map() 31 | if err != nil { 32 | t.Error("failed to assert that no error occurred") 33 | } 34 | assertEnv(t, e) 35 | } 36 | 37 | func assertEnv(t *testing.T, env env.Env) { 38 | assert.Equal(t, travisEnv["TRAVIS_BUILD_DIR"], env.Filepath) 39 | assert.Equal(t, Integration, env.Integration) 40 | assert.NotEmpty(t, env.Author) 41 | assert.Equal(t, travisEnv["TRAVIS_BRANCH"], env.Branch) 42 | assert.Equal(t, "https://github.com/debricked/cli", env.RepositoryUrl) 43 | assert.Equal(t, travisEnv["TRAVIS_COMMIT"], env.Commit) 44 | assert.Equal(t, "debricked/cli", env.Repository) 45 | } 46 | -------------------------------------------------------------------------------- /internal/ci/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "os" 4 | 5 | func EnvKeyIsSet(key string) bool { 6 | value, isPresent := os.LookupEnv(key) 7 | if isPresent && len(value) > 0 { 8 | return true 9 | } 10 | 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /internal/ci/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/debricked/cli/internal/ci/testdata" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEnvKeyIsSet(t *testing.T) { 12 | envKey := "DEBRICKED_CLI_KEY" 13 | assert.False(t, EnvKeyIsSet(envKey), "failed to assert that env key was not set") 14 | 15 | _ = os.Setenv(envKey, "") 16 | defer testdata.UnsetEnvVar(t, envKey) 17 | 18 | assert.False(t, EnvKeyIsSet(envKey), "failed to assert that env key lacked value") 19 | 20 | _ = os.Setenv(envKey, "value") 21 | assert.True(t, EnvKeyIsSet(envKey), "failed to assert that env key was set") 22 | } 23 | -------------------------------------------------------------------------------- /internal/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/hashicorp/go-retryablehttp" 7 | ) 8 | 9 | type IClient interface { 10 | Do(req *retryablehttp.Request) (*http.Response, error) 11 | Post(url, bodyType string, body interface{}) (*http.Response, error) 12 | } 13 | -------------------------------------------------------------------------------- /internal/client/request_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "testing" 7 | 8 | testdataClient "github.com/debricked/cli/internal/client/testdata/client" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGetNilRes(t *testing.T) { 13 | clientMock := testdataClient.NewMock() 14 | debClient := NewDebClient(nil, clientMock) 15 | 16 | response, err := get("", debClient, true, "") //nolint:bodyclose 17 | 18 | assert.ErrorIs(t, NoResErr, err) 19 | assert.Nil(t, response) 20 | } 21 | 22 | func TestPostNilRes(t *testing.T) { 23 | clientMock := testdataClient.NewMock() 24 | debClient := NewDebClient(nil, clientMock) 25 | 26 | response, err := post("", debClient, "application/json", bytes.NewBuffer(nil), true) //nolint:bodyclose 27 | assert.ErrorIs(t, NoResErr, err) 28 | assert.Nil(t, response) 29 | } 30 | 31 | func TestInterpretNilRes(t *testing.T) { 32 | response, err := interpret(nil, func() (*http.Response, error) { //nolint:bodyclose 33 | return nil, NoResErr 34 | }, nil, true) 35 | 36 | assert.Nil(t, response) 37 | assert.ErrorIs(t, NoResErr, err) 38 | } 39 | -------------------------------------------------------------------------------- /internal/client/retry.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/hashicorp/go-retryablehttp" 7 | ) 8 | 9 | func NewRetryClient() *retryablehttp.Client { 10 | client := retryablehttp.NewClient() 11 | client.RetryMax = 3 12 | client.RetryWaitMax = time.Second * 15 13 | client.RetryWaitMin = time.Second * 3 14 | client.Logger = nil 15 | 16 | return client 17 | } 18 | -------------------------------------------------------------------------------- /internal/client/retry_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewRetryClient(t *testing.T) { 9 | c := NewRetryClient() 10 | if c.RetryMax != 3 { 11 | t.Errorf("failed to assert that RetryMax was %d", c.RetryMax) 12 | } 13 | if c.RetryWaitMax.Seconds() != (time.Second * 15).Seconds() { 14 | t.Errorf("failed to assert that RetryWaitMax was %f", (time.Second * 15).Seconds()) 15 | } 16 | if c.RetryWaitMin.Seconds() != (time.Second * 3).Seconds() { 17 | t.Errorf("failed to assert that RetryWaitMin was %f", (time.Second * 3).Seconds()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /internal/cmd/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/auth" 5 | "github.com/debricked/cli/internal/cmd/auth/login" 6 | "github.com/debricked/cli/internal/cmd/auth/logout" 7 | "github.com/debricked/cli/internal/cmd/auth/token" 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func NewAuthCmd(authenticator auth.IAuthenticator) *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "auth", 15 | Short: "Debricked authentication.", 16 | Long: `Debricked service authentication.`, 17 | PreRun: func(cmd *cobra.Command, _ []string) { 18 | _ = viper.BindPFlags(cmd.Flags()) 19 | }, 20 | } 21 | cmd.AddCommand(login.NewLoginCmd(authenticator)) 22 | cmd.AddCommand(logout.NewLogoutCmd(authenticator)) 23 | cmd.AddCommand(token.NewTokenCmd(authenticator)) 24 | 25 | return cmd 26 | } 27 | -------------------------------------------------------------------------------- /internal/cmd/auth/auth_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/auth" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewAuthCmd(t *testing.T) { 11 | authenticator := auth.NewDebrickedAuthenticator("") 12 | cmd := NewAuthCmd(authenticator) 13 | commands := cmd.Commands() 14 | nbrOfCommands := 3 15 | assert.Lenf(t, commands, nbrOfCommands, "failed to assert that there were %d sub commands connected", nbrOfCommands) 16 | } 17 | 18 | func TestPreRun(t *testing.T) { 19 | cmd := NewAuthCmd(nil) 20 | cmd.PreRun(cmd, nil) 21 | } 22 | -------------------------------------------------------------------------------- /internal/cmd/auth/login/login.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/auth" 7 | "github.com/fatih/color" 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func NewLoginCmd(authenticator auth.IAuthenticator) *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "login", 15 | Short: "Authenticate debricked user", 16 | Long: `Start authentication flow to generate access token.`, 17 | PreRun: func(cmd *cobra.Command, _ []string) { 18 | _ = viper.BindPFlags(cmd.Flags()) 19 | }, 20 | RunE: RunE(authenticator), 21 | } 22 | 23 | return cmd 24 | } 25 | 26 | func RunE(a auth.IAuthenticator) func(_ *cobra.Command, args []string) error { 27 | return func(cmd *cobra.Command, _ []string) error { 28 | err := a.Authenticate() 29 | if err != nil { 30 | return err 31 | } 32 | fmt.Printf( 33 | "%s Successfully authenticated\n", 34 | color.GreenString("✔"), 35 | ) 36 | 37 | return nil 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/cmd/auth/login/login_test.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/auth" 7 | "github.com/debricked/cli/internal/auth/testdata" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewLoginCmd(t *testing.T) { 12 | authenticator := auth.NewDebrickedAuthenticator("") 13 | cmd := NewLoginCmd(authenticator) 14 | commands := cmd.Commands() 15 | nbrOfCommands := 0 16 | assert.Len(t, commands, nbrOfCommands) 17 | } 18 | 19 | func TestPreRun(t *testing.T) { 20 | mockAuthenticator := testdata.MockAuthenticator{} 21 | cmd := NewLoginCmd(mockAuthenticator) 22 | cmd.PreRun(cmd, nil) 23 | } 24 | 25 | func TestRunE(t *testing.T) { 26 | a := testdata.MockAuthenticator{} 27 | runE := RunE(a) 28 | 29 | err := runE(nil, []string{}) 30 | 31 | assert.NoError(t, err) 32 | } 33 | 34 | func TestRunEError(t *testing.T) { 35 | a := testdata.ErrorMockAuthenticator{} 36 | runE := RunE(a) 37 | 38 | err := runE(nil, []string{}) 39 | 40 | assert.Error(t, err) 41 | } 42 | -------------------------------------------------------------------------------- /internal/cmd/auth/logout/logout.go: -------------------------------------------------------------------------------- 1 | package logout 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/auth" 7 | "github.com/fatih/color" 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func NewLogoutCmd(authenticator auth.IAuthenticator) *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "logout", 15 | Short: "Logout debricked user", 16 | Long: `Remove cached credentials to logout debricked user.`, 17 | PreRun: func(cmd *cobra.Command, _ []string) { 18 | _ = viper.BindPFlags(cmd.Flags()) 19 | }, 20 | RunE: RunE(authenticator), 21 | } 22 | 23 | return cmd 24 | } 25 | 26 | func RunE(a auth.IAuthenticator) func(_ *cobra.Command, args []string) error { 27 | return func(cmd *cobra.Command, _ []string) error { 28 | err := a.Logout() 29 | if err != nil { 30 | return err 31 | } 32 | fmt.Printf( 33 | "%s Successfully removed credentials\n", 34 | color.GreenString("✔"), 35 | ) 36 | 37 | return nil 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/cmd/auth/logout/logout_test.go: -------------------------------------------------------------------------------- 1 | package logout 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/auth" 7 | "github.com/debricked/cli/internal/auth/testdata" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewLogoutCmd(t *testing.T) { 12 | authenticator := auth.NewDebrickedAuthenticator("") 13 | cmd := NewLogoutCmd(authenticator) 14 | commands := cmd.Commands() 15 | nbrOfCommands := 0 16 | assert.Len(t, commands, nbrOfCommands) 17 | } 18 | 19 | func TestPreRun(t *testing.T) { 20 | mockAuthenticator := testdata.MockAuthenticator{} 21 | cmd := NewLogoutCmd(mockAuthenticator) 22 | cmd.PreRun(cmd, nil) 23 | } 24 | 25 | func TestRunE(t *testing.T) { 26 | a := testdata.MockAuthenticator{} 27 | runE := RunE(a) 28 | 29 | err := runE(nil, []string{}) 30 | 31 | assert.NoError(t, err) 32 | } 33 | 34 | func TestRunEError(t *testing.T) { 35 | a := testdata.ErrorMockAuthenticator{} 36 | runE := RunE(a) 37 | 38 | err := runE(nil, []string{}) 39 | 40 | assert.Error(t, err) 41 | } 42 | -------------------------------------------------------------------------------- /internal/cmd/cmderror/error.go: -------------------------------------------------------------------------------- 1 | package cmderror 2 | 3 | type CommandError struct { 4 | Code int 5 | Err error 6 | } 7 | 8 | func (e CommandError) Error() string { 9 | return e.Err.Error() 10 | } 11 | -------------------------------------------------------------------------------- /internal/cmd/files/files.go: -------------------------------------------------------------------------------- 1 | package files 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/cmd/files/find" 5 | "github.com/debricked/cli/internal/file" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func NewFilesCmd(finder file.IFinder) *cobra.Command { 11 | cmd := &cobra.Command{ 12 | Use: "files", 13 | Short: "Analyze files", 14 | Long: "Analyze files", 15 | PreRun: func(cmd *cobra.Command, _ []string) { 16 | _ = viper.BindPFlags(cmd.Flags()) 17 | }, 18 | } 19 | 20 | cmd.AddCommand(find.NewFindCmd(finder)) 21 | 22 | return cmd 23 | } 24 | -------------------------------------------------------------------------------- /internal/cmd/files/files_test.go: -------------------------------------------------------------------------------- 1 | package files 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/file" 7 | "github.com/debricked/cli/internal/io" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewFilesCmd(t *testing.T) { 12 | finder, _ := file.NewFinder(nil, io.FileSystem{}) 13 | cmd := NewFilesCmd(finder) 14 | commands := cmd.Commands() 15 | nbrOfCommands := 1 16 | assert.Lenf(t, commands, nbrOfCommands, "failed to assert that there were %d sub commands connected", nbrOfCommands) 17 | } 18 | 19 | func TestPreRun(t *testing.T) { 20 | cmd := NewFilesCmd(nil) 21 | cmd.PreRun(cmd, nil) 22 | } 23 | -------------------------------------------------------------------------------- /internal/cmd/report/report.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/cmd/report/license" 5 | "github.com/debricked/cli/internal/cmd/report/sbom" 6 | "github.com/debricked/cli/internal/cmd/report/vulnerability" 7 | licenseReport "github.com/debricked/cli/internal/report/license" 8 | sbomReport "github.com/debricked/cli/internal/report/sbom" 9 | vulnerabilityReport "github.com/debricked/cli/internal/report/vulnerability" 10 | "github.com/spf13/cobra" 11 | "github.com/spf13/viper" 12 | ) 13 | 14 | func NewReportCmd( 15 | licenseReporter licenseReport.Reporter, 16 | vulnerabilityReporter vulnerabilityReport.Reporter, 17 | sbomReporter sbomReport.Reporter, 18 | ) *cobra.Command { 19 | cmd := &cobra.Command{ 20 | Use: "export", 21 | Short: "Generate exports for vulnerabilities, licenses, and SBOM.", 22 | Long: `Generate exports. 23 | Premium is required for license and vulnerability exports. Enterprise is required for SBOM exports. Please visit https://debricked.com/pricing/ for more info.`, 24 | PreRun: func(cmd *cobra.Command, _ []string) { 25 | _ = viper.BindPFlags(cmd.Flags()) 26 | }, 27 | } 28 | 29 | cmd.AddCommand(license.NewLicenseCmd(licenseReporter)) 30 | cmd.AddCommand(vulnerability.NewVulnerabilityCmd(vulnerabilityReporter)) 31 | cmd.AddCommand(sbom.NewSBOMCmd(sbomReporter)) 32 | 33 | return cmd 34 | } 35 | -------------------------------------------------------------------------------- /internal/cmd/report/report_test.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/report/license" 7 | "github.com/debricked/cli/internal/report/sbom" 8 | "github.com/debricked/cli/internal/report/vulnerability" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestNewReportCmd(t *testing.T) { 13 | cmd := NewReportCmd(license.Reporter{}, vulnerability.Reporter{}, sbom.Reporter{}) 14 | commands := cmd.Commands() 15 | nbrOfCommands := 3 16 | assert.Lenf(t, commands, nbrOfCommands, "failed to assert that there were %d sub commands connected", nbrOfCommands) 17 | } 18 | 19 | func TestPreRun(t *testing.T) { 20 | var licenseReporter license.Reporter 21 | var vulnReporter vulnerability.Reporter 22 | var sbomReporter sbom.Reporter 23 | cmd := NewReportCmd(licenseReporter, vulnReporter, sbomReporter) 24 | cmd.PreRun(cmd, nil) 25 | } 26 | -------------------------------------------------------------------------------- /internal/cmd/report/testdata/reporter_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "github.com/debricked/cli/internal/report" 4 | 5 | type ReporterMock struct { 6 | err error 7 | } 8 | 9 | func NewReporterMock() *ReporterMock { 10 | return &ReporterMock{nil} 11 | } 12 | 13 | func (r *ReporterMock) Order(_ report.IOrderArgs) error { 14 | return r.err 15 | } 16 | 17 | func (r *ReporterMock) SetError(e error) { 18 | r.err = e 19 | } 20 | -------------------------------------------------------------------------------- /internal/cmd/root/root_test.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/wire" 7 | "github.com/spf13/viper" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewRootCmd(t *testing.T) { 12 | cmd := NewRootCmd("v0.0.0", wire.GetCliContainer()) 13 | commands := cmd.Commands() 14 | nbrOfCommands := 7 15 | if len(commands) != nbrOfCommands { 16 | t.Errorf( 17 | "failed to assert that there were %d sub commands connected (was %d)", 18 | nbrOfCommands, 19 | len(commands), 20 | ) 21 | } 22 | 23 | flags := cmd.PersistentFlags() 24 | flag := flags.Lookup(OldAccessTokenFlag) 25 | assert.NotNil(t, flag) 26 | assert.Equal(t, "t", flag.Shorthand) 27 | 28 | match := false 29 | viperKeys := viper.AllKeys() 30 | for _, key := range viperKeys { 31 | if key == AccessTokenFlag { 32 | match = true 33 | 34 | break 35 | } 36 | } 37 | assert.Truef(t, match, "failed to assert that flag was present: "+OldAccessTokenFlag) 38 | assert.Len(t, viperKeys, 23) 39 | } 40 | 41 | func TestPreRun(t *testing.T) { 42 | cmd := NewRootCmd("", wire.GetCliContainer()) 43 | cmd.PreRun(cmd, nil) 44 | } 45 | -------------------------------------------------------------------------------- /internal/cmd/scan/testdata/npm/package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /internal/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/fatih/color" 8 | ) 9 | 10 | func Log(message string, debug bool) { 11 | if debug { 12 | DebugLogger := log.New(os.Stderr, "DEBUG: ", log.Ldate|log.Ltime) 13 | DebugLogger.Println(color.BlueString(message)) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/debug/debug_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestLog(t *testing.T) { 12 | rescueStderr := os.Stderr 13 | r, w, _ := os.Pipe() 14 | os.Stderr = w 15 | 16 | Log("hello", true) 17 | 18 | _ = w.Close() 19 | output, _ := io.ReadAll(r) 20 | os.Stderr = rescueStderr 21 | 22 | assert.Contains(t, string(output), "DEBUG: ") 23 | assert.Contains(t, string(output), "hello\n") 24 | } 25 | 26 | func TestLogDebugDisabled(t *testing.T) { 27 | rescueStderr := os.Stderr 28 | r, w, _ := os.Pipe() 29 | os.Stderr = w 30 | 31 | Log("hello", false) 32 | 33 | _ = w.Close() 34 | output, _ := io.ReadAll(r) 35 | os.Stderr = rescueStderr 36 | 37 | assert.Empty(t, string(output)) 38 | } 39 | -------------------------------------------------------------------------------- /internal/file/testdata/composer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viktigpetterr/composer", 3 | "authors": [ 4 | { 5 | "name": "viktigpetterr", 6 | "email": "viktor.grasljunga@gmail.com" 7 | } 8 | ], 9 | "require-dev": { 10 | "phpunit/phpunit": "^9.5" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/file/testdata/finder_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/file" 5 | ) 6 | 7 | type FinderMock struct { 8 | groups file.Groups 9 | compiledFormats []*file.CompiledFormat 10 | error error 11 | } 12 | 13 | func NewFinderMock() *FinderMock { 14 | return &FinderMock{ 15 | groups: file.Groups{}, 16 | compiledFormats: nil, 17 | error: nil, 18 | } 19 | } 20 | 21 | // GetGroups return all file groups in specified path recursively. 22 | func (f *FinderMock) GetGroups(_ file.DebrickedOptions) (file.Groups, error) { 23 | return f.groups, f.error 24 | } 25 | 26 | func (f *FinderMock) GetConfigPath(_ string, _ []string, _ []string) string { 27 | return "" 28 | } 29 | 30 | func (f *FinderMock) GetSupportedFormats() ([]*file.CompiledFormat, error) { 31 | return f.compiledFormats, f.error 32 | } 33 | 34 | func (f *FinderMock) SetGetGroupsReturnMock(gs file.Groups, err error) { 35 | f.groups = gs 36 | f.error = err 37 | } 38 | 39 | func (f *FinderMock) SetGetSupportedFormatsReturnMock(compiledFormats []*file.CompiledFormat, err error) { 40 | f.compiledFormats = compiledFormats 41 | f.error = err 42 | } 43 | -------------------------------------------------------------------------------- /internal/file/testdata/misc/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "hello_world" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "regex 1.5.0 (git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831)", 6 | ] 7 | 8 | [[package]] 9 | name = "regex" 10 | version = "1.5.0" 11 | source = "git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831" 12 | -------------------------------------------------------------------------------- /internal/file/testdata/misc/composer.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/misc/composer.json -------------------------------------------------------------------------------- /internal/file/testdata/misc/composer.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/misc/composer.lock -------------------------------------------------------------------------------- /internal/file/testdata/misc/debricked-config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/misc/debricked-config.yaml -------------------------------------------------------------------------------- /internal/file/testdata/misc/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/debricked/cli 2 | 3 | go 1.20 4 | 5 | require ( 6 | ) 7 | -------------------------------------------------------------------------------- /internal/file/testdata/misc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "browserslist": [ 3 | "> 1%" 4 | ], 5 | "dependencies": {}, 6 | "private": true, 7 | "version": "3.0.0" 8 | } 9 | -------------------------------------------------------------------------------- /internal/file/testdata/misc/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/misc/requirements.txt -------------------------------------------------------------------------------- /internal/file/testdata/misc/zipped.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/misc/zipped.zip -------------------------------------------------------------------------------- /internal/file/testdata/pip/requirements-dev.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/pip/requirements-dev.txt -------------------------------------------------------------------------------- /internal/file/testdata/pip/requirements-dev.txt.pip.debricked.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/pip/requirements-dev.txt.pip.debricked.lock -------------------------------------------------------------------------------- /internal/file/testdata/pip/requirements.test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/pip/requirements.test.txt -------------------------------------------------------------------------------- /internal/file/testdata/pip/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/pip/requirements.txt -------------------------------------------------------------------------------- /internal/file/testdata/pip/requirements.txt.pip.debricked.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/pip/requirements.txt.pip.debricked.lock -------------------------------------------------------------------------------- /internal/file/testdata/test/test-file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/file/testdata/test/test-file -------------------------------------------------------------------------------- /internal/file/testdata/workspace/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-workspaces-test", 3 | "private": true, 4 | "devDependencies": { 5 | "prettier": "3.3.3", 6 | "rimraf": "6.0.1", 7 | "typescript": "5.5.4" 8 | }, 9 | "workspaces": [ 10 | "packages/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /internal/file/testdata/workspace/common/packages/package_one/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "package_one", 3 | "version": "1.0.0", 4 | "description": "testing workspaces", 5 | "main": "lib/index.js", 6 | "keywords": [], 7 | "author": "test", 8 | "license": "MIT", 9 | "dependencies": { 10 | "@types/node": "^20.0.0" 11 | }, 12 | "devDependencies": { 13 | "typescript": "5.5.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/file/testdata/workspace/common/packages/package_two/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "package_two", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "main": "lib/main.js", 6 | "keywords": [], 7 | "author": "test", 8 | "license": "MIT", 9 | "dependencies": { 10 | "package_one": "^1.0.0", 11 | "@types/node": "16.11.40", 12 | "minimist": "^1.2.5" 13 | }, 14 | "devDependencies": { 15 | "typescript": "5.5.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/file/testdata/workspace/rare/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-workspaces-test", 3 | "private": true, 4 | "devDependencies": { 5 | "prettier": "3.3.3", 6 | "rimraf": "6.0.1", 7 | "typescript": "5.5.4" 8 | }, 9 | "workspaces": { 10 | "packages": [ 11 | "packages/*" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/file/testdata/workspace/rare/packages/package_one/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "package_one", 3 | "version": "1.0.0", 4 | "description": "testing workspaces", 5 | "main": "lib/index.js", 6 | "keywords": [], 7 | "author": "test", 8 | "license": "MIT", 9 | "dependencies": { 10 | "@types/node": "^20.0.0" 11 | }, 12 | "devDependencies": { 13 | "typescript": "5.5.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/file/testdata/workspace/rare/packages/package_two/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "package_two", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "main": "lib/main.js", 6 | "keywords": [], 7 | "author": "test", 8 | "license": "MIT", 9 | "dependencies": { 10 | "package_one": "^1.0.0", 11 | "@types/node": "16.11.40", 12 | "minimist": "^1.2.5" 13 | }, 14 | "devDependencies": { 15 | "typescript": "5.5.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/fingerprint/testdata/archive/bz2/stuf-0.1.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/archive/bz2/stuf-0.1.tar.bz2 -------------------------------------------------------------------------------- /internal/fingerprint/testdata/archive/jar/log4j-api-2.18.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/archive/jar/log4j-api-2.18.0.jar -------------------------------------------------------------------------------- /internal/fingerprint/testdata/archive/nupkg/newtonsoft.json.13.0.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/archive/nupkg/newtonsoft.json.13.0.3.nupkg -------------------------------------------------------------------------------- /internal/fingerprint/testdata/archive/tgz/lodash.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/archive/tgz/lodash.tgz -------------------------------------------------------------------------------- /internal/fingerprint/testdata/archive/whl/requests-2.31.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/archive/whl/requests-2.31.0-py3-none-any.whl -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter/nofile.txt: -------------------------------------------------------------------------------- 1 | xxx -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter/something.resx: -------------------------------------------------------------------------------- 1 | Some text to make it larger than a few bytesSome text to make it larger than a few bytesSome text to make it larger than a few bytesSome text to make it larger than a few bytesSome text to make it larger than a few bytes 2 | -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter/subdirectory/mvnw.cmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/fingerprint/testdata/fingerprinter/subdirectory/mvnw.cmd -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter/testfile.py: -------------------------------------------------------------------------------- 1 | print("hello world") 2 | -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter/wfailing.jar: -------------------------------------------------------------------------------- 1 | xxx -------------------------------------------------------------------------------- /internal/fingerprint/testdata/fingerprinter_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/fingerprint" 5 | ) 6 | 7 | type FingerprintMock struct { 8 | error error 9 | } 10 | 11 | func NewFingerprintMock() *FingerprintMock { 12 | return &FingerprintMock{ 13 | error: nil, 14 | } 15 | } 16 | 17 | func NewFingerprintMockFileExistsError() *FingerprintMock { 18 | return &FingerprintMock{ 19 | error: &fingerprint.FingerprintFileExistsError{}, 20 | } 21 | } 22 | 23 | func (f *FingerprintMock) FingerprintFiles( 24 | options fingerprint.DebrickedOptions, 25 | ) (fingerprint.Fingerprints, error) { 26 | return fingerprint.Fingerprints{}, f.error 27 | } 28 | -------------------------------------------------------------------------------- /internal/io/err/error.go: -------------------------------------------------------------------------------- 1 | package err 2 | 3 | type IError interface { 4 | error 5 | } 6 | -------------------------------------------------------------------------------- /internal/io/err/errors.go: -------------------------------------------------------------------------------- 1 | package err 2 | 3 | type IErrors interface { 4 | Warning(err IError) 5 | Critical(err IError) 6 | GetWarningErrors() []IError 7 | GetCriticalErrors() []IError 8 | GetAll() []IError 9 | HasError() bool 10 | } 11 | 12 | type Errors struct { 13 | title string 14 | warningErrs []IError 15 | criticalErrs []IError 16 | } 17 | 18 | func NewErrors(title string) *Errors { 19 | return &Errors{ 20 | title: title, 21 | warningErrs: []IError{}, 22 | criticalErrs: []IError{}, 23 | } 24 | } 25 | 26 | func (errors *Errors) Warning(err IError) { 27 | errors.warningErrs = append(errors.warningErrs, err) 28 | } 29 | 30 | func (errors *Errors) Critical(err IError) { 31 | errors.criticalErrs = append(errors.criticalErrs, err) 32 | } 33 | 34 | func (errors *Errors) GetWarningErrors() []IError { 35 | return errors.warningErrs 36 | } 37 | 38 | func (errors *Errors) GetCriticalErrors() []IError { 39 | return errors.criticalErrs 40 | } 41 | 42 | func (errors *Errors) GetAll() []IError { 43 | return append(errors.warningErrs, errors.criticalErrs...) 44 | } 45 | 46 | func (errors *Errors) HasError() bool { 47 | return len(errors.criticalErrs) > 0 || len(errors.warningErrs) > 0 48 | } 49 | -------------------------------------------------------------------------------- /internal/io/file_writer.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type IFileWriter interface { 8 | Write(file *os.File, p []byte) error 9 | Create(name string) (*os.File, error) 10 | Close(file *os.File) error 11 | } 12 | 13 | type FileWriter struct{} 14 | 15 | func (fw FileWriter) Create(name string) (*os.File, error) { 16 | return os.Create(name) 17 | } 18 | 19 | func (fw FileWriter) Write(file *os.File, p []byte) error { 20 | _, err := file.Write(p) 21 | 22 | return err 23 | } 24 | 25 | func (fw FileWriter) Close(file *os.File) error { 26 | return file.Close() 27 | } 28 | -------------------------------------------------------------------------------- /internal/io/file_writer_test.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var fw = FileWriter{} 11 | 12 | const fileName = "debricked-test.json" 13 | 14 | func TestCreate(t *testing.T) { 15 | fn := fileName + t.Name() 16 | testFile, err := fw.Create(fn) 17 | assert.NoError(t, err) 18 | assert.NotNil(t, testFile) 19 | defer deleteFile(t, testFile) 20 | } 21 | 22 | func TestWrite(t *testing.T) { 23 | fn := fileName + t.Name() 24 | content := []byte("{}") 25 | testFile, _ := fw.Create(fn) 26 | defer deleteFile(t, testFile) 27 | 28 | err := fw.Write(testFile, content) 29 | 30 | assert.NoError(t, err) 31 | fileContents, err := os.ReadFile(fn) 32 | assert.NoError(t, err) 33 | assert.Equal(t, fileContents, content) 34 | } 35 | 36 | func TestClose(t *testing.T) { 37 | fn := fileName + t.Name() 38 | testFile, _ := fw.Create(fn) 39 | defer deleteFile(t, testFile) 40 | 41 | err := fw.Close(testFile) 42 | 43 | assert.NoError(t, err) 44 | } 45 | 46 | func deleteFile(t *testing.T, file *os.File) { 47 | _ = file.Close() 48 | err := os.Remove(file.Name()) 49 | assert.NoError(t, err) 50 | } 51 | -------------------------------------------------------------------------------- /internal/io/testdata/archive_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "strings" 4 | 5 | type ArchiveMock struct { 6 | ZipFileError error 7 | B64Error error 8 | CleanupError error 9 | PathError error 10 | Dir string 11 | UnzipFileError error 12 | } 13 | 14 | func (am ArchiveMock) ZipFile(sourceName string, targetName string, zipName string) error { 15 | if !strings.HasPrefix(sourceName, am.Dir) || !strings.HasPrefix(targetName, am.Dir) { 16 | return am.PathError 17 | } 18 | return am.ZipFileError 19 | 20 | } 21 | 22 | func (am ArchiveMock) B64(sourceName string, targetName string) error { 23 | return am.B64Error 24 | 25 | } 26 | 27 | func (am ArchiveMock) Cleanup(fileName string) error { 28 | return am.CleanupError 29 | } 30 | 31 | func (am ArchiveMock) UnzipFile(sourcePath string, targetPath string) error { 32 | return am.UnzipFileError 33 | } 34 | -------------------------------------------------------------------------------- /internal/io/testdata/embed-file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/io/testdata/embed-file -------------------------------------------------------------------------------- /internal/io/testdata/file_writer_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type FileWriterMock struct { 8 | file *os.File 9 | Contents []byte 10 | CreateErr error 11 | WriteErr error 12 | CloseErr error 13 | } 14 | 15 | func (fw *FileWriterMock) Create(_ string) (*os.File, error) { 16 | return fw.file, fw.CreateErr 17 | } 18 | 19 | func (fw *FileWriterMock) Write(_ *os.File, bytes []byte) error { 20 | fw.Contents = append(fw.Contents, bytes...) 21 | 22 | return fw.WriteErr 23 | } 24 | 25 | func (fw *FileWriterMock) Close(_ *os.File) error { 26 | return fw.CloseErr 27 | } 28 | -------------------------------------------------------------------------------- /internal/io/testdata/text.txt: -------------------------------------------------------------------------------- 1 | text -------------------------------------------------------------------------------- /internal/io/testdata/text.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/io/testdata/text.zip -------------------------------------------------------------------------------- /internal/report/report.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | type IReporter interface { 4 | Order(args IOrderArgs) error 5 | } 6 | 7 | type IOrderArgs interface{} 8 | -------------------------------------------------------------------------------- /internal/report/vulnerability/report.go: -------------------------------------------------------------------------------- 1 | package vulnerability 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/debricked/cli/internal/client" 9 | "github.com/debricked/cli/internal/report" 10 | ) 11 | 12 | var ( 13 | ArgsError = errors.New("failed to handle args") 14 | SubscriptionError = errors.New("premium feature. Please visit https://debricked.com/pricing/ for more info") 15 | ) 16 | 17 | type OrderArgs struct { 18 | Email string 19 | } 20 | 21 | type Reporter struct { 22 | DebClient client.IDebClient 23 | } 24 | 25 | func (r Reporter) Order(args report.IOrderArgs) error { 26 | orderArgs, ok := args.(OrderArgs) 27 | if !ok { 28 | return ArgsError 29 | } 30 | 31 | uri := fmt.Sprintf("/api/1.0/open/repositories/get-repositories?order=asc&generateExcel=1&email=%s", orderArgs.Email) 32 | res, err := r.DebClient.Get(uri, "application/json") 33 | if err != nil { 34 | return err 35 | } 36 | defer res.Body.Close() 37 | if res.StatusCode == http.StatusForbidden { 38 | return SubscriptionError 39 | } 40 | 41 | if res.StatusCode != http.StatusOK { 42 | return fmt.Errorf("failed to order report. Status code: %d", res.StatusCode) 43 | } 44 | 45 | return nil 46 | 47 | } 48 | -------------------------------------------------------------------------------- /internal/resolution/file/file_batch.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import "github.com/debricked/cli/internal/resolution/pm" 4 | 5 | type IBatch interface { 6 | Files() []string 7 | Add(file string) 8 | Pm() pm.IPm 9 | } 10 | 11 | type Batch struct { 12 | files map[string]bool 13 | pm pm.IPm 14 | } 15 | 16 | func NewBatch(pm pm.IPm) Batch { 17 | return Batch{files: make(map[string]bool), pm: pm} 18 | } 19 | 20 | func (b Batch) Files() []string { 21 | var files []string 22 | for file := range b.files { 23 | files = append(files, file) 24 | } 25 | 26 | return files 27 | } 28 | 29 | func (b Batch) Add(file string) { 30 | if ok := b.files[file]; !ok { 31 | b.files[file] = true 32 | } 33 | } 34 | 35 | func (b Batch) Pm() pm.IPm { 36 | return b.pm 37 | } 38 | -------------------------------------------------------------------------------- /internal/resolution/file/file_batch_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/resolution/pm/testdata" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewBatch(t *testing.T) { 11 | b := NewBatch(nil) 12 | assert.NotNil(t, b) 13 | 14 | b = NewBatch(testdata.PmMock{}) 15 | assert.NotNil(t, b) 16 | } 17 | 18 | func TestFiles(t *testing.T) { 19 | b := NewBatch(testdata.PmMock{}) 20 | 21 | files := b.Files() 22 | assert.Empty(t, files) 23 | 24 | b.Add("file-1") 25 | assert.Len(t, b.Files(), 1) 26 | 27 | b.Add("file-1") 28 | assert.Len(t, b.Files(), 1) 29 | 30 | b.Add("file-2") 31 | assert.Len(t, b.Files(), 2) 32 | } 33 | 34 | func TestAdd(t *testing.T) { 35 | b := NewBatch(testdata.PmMock{}) 36 | 37 | filesMap := b.files 38 | assert.Empty(t, filesMap) 39 | 40 | b.Add("file-1") 41 | filesMap = b.files 42 | assert.Len(t, filesMap, 1) 43 | 44 | b.Add("file-1") 45 | filesMap = b.files 46 | assert.Len(t, filesMap, 1) 47 | 48 | b.Add("file-2") 49 | filesMap = b.files 50 | assert.Len(t, filesMap, 2) 51 | } 52 | 53 | func TestPm(t *testing.T) { 54 | b := NewBatch(nil) 55 | assert.Nil(t, b.Pm()) 56 | 57 | pm := testdata.PmMock{} 58 | b = NewBatch(pm) 59 | assert.Equal(t, pm, b.Pm()) 60 | } 61 | -------------------------------------------------------------------------------- /internal/resolution/file/testdata/file_batch_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/file" 5 | "github.com/debricked/cli/internal/resolution/pm" 6 | ) 7 | 8 | type BatchFactoryMock struct { 9 | pms []pm.IPm 10 | } 11 | 12 | func NewBatchFactoryMock() BatchFactoryMock { 13 | return BatchFactoryMock{ 14 | pms: pm.Pms(), 15 | } 16 | } 17 | 18 | func (bf BatchFactoryMock) SetNpmPreferred(_ bool) { 19 | } 20 | 21 | func (bf BatchFactoryMock) Make(_ []string) []file.IBatch { 22 | 23 | return []file.IBatch{} 24 | } 25 | -------------------------------------------------------------------------------- /internal/resolution/file/testdata/file_batch_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | -------------------------------------------------------------------------------- /internal/resolution/job/base_job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "errors" 5 | "os/exec" 6 | "strings" 7 | ) 8 | 9 | type BaseJob struct { 10 | file string 11 | errs IErrors 12 | status chan string 13 | } 14 | 15 | func NewBaseJob(file string) BaseJob { 16 | return BaseJob{ 17 | file: file, 18 | errs: NewErrors(file), 19 | status: make(chan string), 20 | } 21 | } 22 | 23 | func (j *BaseJob) GetFile() string { 24 | return j.file 25 | } 26 | 27 | func (j *BaseJob) Errors() IErrors { 28 | return j.errs 29 | } 30 | 31 | func (j *BaseJob) ReceiveStatus() chan string { 32 | return j.status 33 | } 34 | 35 | func (j *BaseJob) SendStatus(status string) { 36 | j.status <- status 37 | } 38 | 39 | func (j *BaseJob) GetExitError(err error, commandOutput string) error { 40 | exitErr, ok := err.(*exec.ExitError) 41 | if !ok { 42 | return err 43 | } 44 | 45 | // If Stderr is empty, use commandOutput as error string instead 46 | errorMessage := string(exitErr.Stderr) 47 | if errorMessage == "" { 48 | errorMessage = commandOutput 49 | } 50 | 51 | return errors.New(errorMessage) 52 | } 53 | 54 | func (j *BaseJob) GetExecutableNotFoundErrorDocumentation(pm string) string { 55 | return strings.Join( 56 | []string{ 57 | pm + " wasn't found.", 58 | "Please check if it is installed and accessible by the CLI.", 59 | }, " ") 60 | } 61 | -------------------------------------------------------------------------------- /internal/resolution/job/errors.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | type IErrors interface { 4 | Warning(err IError) 5 | Critical(err IError) 6 | Append(err IError) 7 | GetWarningErrors() []IError 8 | GetCriticalErrors() []IError 9 | GetAll() []IError 10 | HasError() bool 11 | } 12 | 13 | type Errors struct { 14 | title string 15 | warningErrs []IError 16 | criticalErrs []IError 17 | } 18 | 19 | func NewErrors(title string) *Errors { 20 | return &Errors{ 21 | title: title, 22 | warningErrs: []IError{}, 23 | criticalErrs: []IError{}, 24 | } 25 | } 26 | 27 | func (errors *Errors) Warning(err IError) { 28 | errors.warningErrs = append(errors.warningErrs, err) 29 | } 30 | 31 | func (errors *Errors) Critical(err IError) { 32 | errors.criticalErrs = append(errors.criticalErrs, err) 33 | } 34 | 35 | func (errors *Errors) Append(err IError) { 36 | if err.IsCritical() { 37 | errors.Critical(err) 38 | } else { 39 | errors.Warning(err) 40 | } 41 | } 42 | 43 | func (errors *Errors) GetWarningErrors() []IError { 44 | return errors.warningErrs 45 | } 46 | 47 | func (errors *Errors) GetCriticalErrors() []IError { 48 | return errors.criticalErrs 49 | } 50 | 51 | func (errors *Errors) GetAll() []IError { 52 | return append(errors.warningErrs, errors.criticalErrs...) 53 | } 54 | 55 | func (errors *Errors) HasError() bool { 56 | return len(errors.criticalErrs) > 0 || len(errors.warningErrs) > 0 57 | } 58 | -------------------------------------------------------------------------------- /internal/resolution/job/job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | type IJob interface { 4 | GetFile() string 5 | Errors() IErrors 6 | Run() 7 | ReceiveStatus() chan string 8 | } 9 | -------------------------------------------------------------------------------- /internal/resolution/job/testdata/job_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/resolution/job" 7 | ) 8 | 9 | type JobMock struct { 10 | file string 11 | errs job.IErrors 12 | status chan string 13 | } 14 | 15 | func (j *JobMock) ReceiveStatus() chan string { 16 | return j.status 17 | } 18 | 19 | func (j *JobMock) GetFile() string { 20 | return j.file 21 | } 22 | 23 | func (j *JobMock) Errors() job.IErrors { 24 | return j.errs 25 | } 26 | 27 | func (j *JobMock) Run() { 28 | fmt.Println("job mock run") 29 | } 30 | 31 | func NewJobMock(file string) *JobMock { 32 | return &JobMock{ 33 | file: file, 34 | status: make(chan string), 35 | errs: job.NewErrors(file), 36 | } 37 | } 38 | 39 | func (j *JobMock) SetErr(err job.IError) { 40 | j.errs.Critical(err) 41 | } 42 | -------------------------------------------------------------------------------- /internal/resolution/job/testdata/job_test_util.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | 8 | "github.com/debricked/cli/internal/resolution/job" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func AssertPathErr(t *testing.T, jobErrs job.IErrors) { 13 | var path string 14 | if runtime.GOOS == "windows" { 15 | path = "%PATH%" 16 | } else { 17 | path = "$PATH" 18 | } 19 | errs := jobErrs.GetAll() 20 | assert.Len(t, errs, 1) 21 | err := errs[0] 22 | errMsg := fmt.Sprintf("executable file not found in %s", path) 23 | assert.ErrorContains(t, err, errMsg) 24 | } 25 | 26 | func WaitStatus(j job.IJob) { 27 | for { 28 | <-j.ReceiveStatus() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/README.md: -------------------------------------------------------------------------------- 1 | # Bower resolution logic 2 | 3 | The way resolution of bower lock files works is as follows: 4 | 5 | 1. Run `bower install --save --save-dev --save-exact --allow-root` in order to install all dependencies 6 | 2. Run `bower list` to get installed dependencies tree 7 | 8 | The result of `bower list` command is then being written into the lock file. 9 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | import ( 4 | "os/exec" 5 | "path/filepath" 6 | ) 7 | 8 | type ICmdFactory interface { 9 | MakeInstallCmd(command string, file string) (*exec.Cmd, error) 10 | MakeListCmd(command string, file string) (*exec.Cmd, error) 11 | } 12 | 13 | type IExecPath interface { 14 | LookPath(file string) (string, error) 15 | } 16 | 17 | type ExecPath struct { 18 | } 19 | 20 | func (ExecPath) LookPath(file string) (string, error) { 21 | return exec.LookPath(file) 22 | } 23 | 24 | type CmdFactory struct { 25 | execPath IExecPath 26 | } 27 | 28 | func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 29 | path, err := cmdf.execPath.LookPath(command) 30 | 31 | fileDir := filepath.Dir(file) 32 | 33 | return &exec.Cmd{ 34 | Path: path, 35 | Args: []string{ 36 | command, 37 | "install", 38 | "--save", 39 | "--save-dev", 40 | "--save-exact", 41 | "--allow-root", 42 | }, 43 | Dir: fileDir, 44 | }, err 45 | } 46 | 47 | func (cmdf CmdFactory) MakeListCmd(command string, file string) (*exec.Cmd, error) { 48 | path, err := cmdf.execPath.LookPath(command) 49 | 50 | fileDir := filepath.Dir(file) 51 | 52 | return &exec.Cmd{ 53 | Path: path, 54 | Args: []string{command, "list"}, 55 | Dir: fileDir, 56 | }, err 57 | } 58 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeInstallCmd(t *testing.T) { 10 | bowerCommand := "bower" 11 | cmd, _ := CmdFactory{ 12 | execPath: ExecPath{}, 13 | }.MakeInstallCmd(bowerCommand, "file") 14 | assert.NotNil(t, cmd) 15 | args := cmd.Args 16 | assert.Contains(t, args, "bower") 17 | assert.Contains(t, args, "install") 18 | assert.Contains(t, args, "--save") 19 | assert.Contains(t, args, "--save-dev") 20 | assert.Contains(t, args, "--save-exact") 21 | assert.Contains(t, args, "--allow-root") 22 | } 23 | 24 | func TestMakeListCmd(t *testing.T) { 25 | bowerCommand := "bower" 26 | cmd, _ := CmdFactory{ 27 | execPath: ExecPath{}, 28 | }.MakeListCmd(bowerCommand, "file") 29 | assert.NotNil(t, cmd) 30 | args := cmd.Args 31 | assert.Contains(t, args, "bower") 32 | assert.Contains(t, args, "list") 33 | } 34 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/pm.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | const Name = "bower" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (Pm) Manifests() []string { 20 | return []string{ 21 | `bower\.json$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/pm_test.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 1) 24 | manifest := manifests[0] 25 | assert.Equal(t, `bower\.json$`, manifest) 26 | _, err := regexp.Compile(manifest) 27 | assert.NoError(t, err) 28 | 29 | cases := map[string]bool{ 30 | "bower.json": true, 31 | "package.json": false, 32 | "package-lock.json": false, 33 | "bower.lock": false, 34 | } 35 | for file, isMatch := range cases { 36 | t.Run(file, func(t *testing.T) { 37 | matched, _ := regexp.MatchString(manifest, file) 38 | assert.Equal(t, isMatch, matched) 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/strategy.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | "github.com/debricked/cli/internal/resolution/pm/writer" 6 | ) 7 | 8 | type Strategy struct { 9 | files []string 10 | } 11 | 12 | func (s Strategy) Invoke() ([]job.IJob, error) { 13 | var jobs []job.IJob 14 | for _, file := range s.files { 15 | jobs = append(jobs, NewJob(file, CmdFactory{execPath: ExecPath{}}, writer.FileWriter{})) 16 | } 17 | 18 | return jobs, nil 19 | } 20 | 21 | func NewStrategy(files []string) Strategy { 22 | return Strategy{files} 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/strategy_test.go: -------------------------------------------------------------------------------- 1 | package bower 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/bower/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "os/exec" 4 | 5 | type CmdFactoryMock struct { 6 | InstallCmdName string 7 | MakeInstallCmdErr error 8 | ListCmdName string 9 | MakeListCmdErr error 10 | } 11 | 12 | func NewEchoCmdFactory() CmdFactoryMock { 13 | return CmdFactoryMock{ 14 | InstallCmdName: "echo", 15 | ListCmdName: "echo", 16 | } 17 | } 18 | 19 | func (f CmdFactoryMock) MakeInstallCmd(_ string, _ string) (*exec.Cmd, error) { 20 | return exec.Command(f.InstallCmdName, "MakeInstallCmd"), f.MakeInstallCmdErr 21 | } 22 | 23 | func (f CmdFactoryMock) MakeListCmd(_ string, _ string) (*exec.Cmd, error) { 24 | return exec.Command(f.ListCmdName, "MakeListCmd"), f.MakeListCmdErr 25 | } 26 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/README.md: -------------------------------------------------------------------------------- 1 | # Composer resolution logic 2 | 3 | The way resolution of composer lock files works is as follows: 4 | 5 | 1. Run `composer update --no-interaction --no-scripts --ignore-platform-reqs --no-autoloader --no-install --no-plugins --no-audit` in order to install all dependencies 6 | 7 | Generated `composer.lock` file is then uploaded together with `composer.json` for scanning. 8 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path/filepath" 7 | ) 8 | 9 | type ICmdFactory interface { 10 | MakeInstallCmd(command string, file string) (*exec.Cmd, error) 11 | } 12 | 13 | type IExecPath interface { 14 | LookPath(file string) (string, error) 15 | } 16 | 17 | type ExecPath struct { 18 | } 19 | 20 | func (ExecPath) LookPath(file string) (string, error) { 21 | return exec.LookPath(file) 22 | } 23 | 24 | type CmdFactory struct { 25 | execPath IExecPath 26 | } 27 | 28 | func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 29 | path, err := cmdf.execPath.LookPath(command) 30 | 31 | fileDir := filepath.Dir(file) 32 | 33 | return &exec.Cmd{ 34 | Path: path, 35 | Args: []string{command, "update", 36 | "--no-interaction", // We can't answer any prompts... 37 | "--no-scripts", // Avoid risky scripts 38 | "--ignore-platform-reqs", // We won't run the code, so we don't care about the platform 39 | "--no-autoloader", // We won't execute any code, no need for autoloader 40 | "--no-install", // No need to install packages 41 | "--no-plugins", // We won't run the code, so no plugins needed 42 | "--no-audit", // We don't want to run an audit 43 | }, 44 | Dir: fileDir, 45 | Env: os.Environ(), 46 | }, err 47 | } 48 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeInstallCmd(t *testing.T) { 10 | composerCommand := "composer" 11 | cmd, err := CmdFactory{ 12 | execPath: ExecPath{}, 13 | }.MakeInstallCmd(composerCommand, "file") 14 | assert.NoError(t, err) 15 | assert.NotNil(t, cmd) 16 | args := cmd.Args 17 | assert.Contains(t, args, "composer") 18 | assert.Contains(t, args, "update") 19 | } 20 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/pm.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | const Name = "composer" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (Pm) Manifests() []string { 20 | return []string{ 21 | `composer\.json$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/pm_test.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 1) 24 | manifest := manifests[0] 25 | assert.Equal(t, `composer\.json$`, manifest) 26 | _, err := regexp.Compile(manifest) 27 | assert.NoError(t, err) 28 | 29 | cases := map[string]bool{ 30 | "composer.json": true, 31 | "composer.lock": false, 32 | "package-lock.json": false, 33 | } 34 | for file, isMatch := range cases { 35 | t.Run(file, func(t *testing.T) { 36 | matched, _ := regexp.MatchString(manifest, file) 37 | assert.Equal(t, isMatch, matched) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/strategy.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type Strategy struct { 8 | files []string 9 | } 10 | 11 | func (s Strategy) Invoke() ([]job.IJob, error) { 12 | var jobs []job.IJob 13 | for _, file := range s.files { 14 | jobs = append(jobs, NewJob( 15 | file, 16 | true, 17 | CmdFactory{ 18 | execPath: ExecPath{}, 19 | }, 20 | ), 21 | ) 22 | } 23 | 24 | return jobs, nil 25 | } 26 | 27 | func NewStrategy(files []string) Strategy { 28 | return Strategy{files} 29 | } 30 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/strategy_test.go: -------------------------------------------------------------------------------- 1 | package composer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/composer/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type CmdFactoryMock struct { 8 | InstallCmdName string 9 | MakeInstallErr error 10 | } 11 | 12 | func NewEchoCmdFactory() CmdFactoryMock { 13 | return CmdFactoryMock{ 14 | InstallCmdName: "echo", 15 | } 16 | } 17 | 18 | func (f CmdFactoryMock) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 19 | return exec.Command(f.InstallCmdName), f.MakeInstallErr 20 | } 21 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/README.md: -------------------------------------------------------------------------------- 1 | # Go resolution logic 2 | 3 | The way resolution of go lock files works is as follows: 4 | 5 | 1. Run `go mod graph` in order to create dependency graph 6 | 2. Run `go list -mod=readonly -e -m all` to get the list of packages 7 | 8 | The results of the commands above are then combined to form the finished lock file. 9 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import "os/exec" 4 | 5 | type ICmdFactory interface { 6 | MakeGraphCmd(workingDirectory string) (*exec.Cmd, error) 7 | MakeListCmd(workingDirectory string) (*exec.Cmd, error) 8 | MakeListJsonCmd(workingDirectory string) (*exec.Cmd, error) 9 | } 10 | 11 | type CmdFactory struct{} 12 | 13 | func (_ CmdFactory) MakeGraphCmd(workingDirectory string) (*exec.Cmd, error) { 14 | path, err := exec.LookPath("go") 15 | 16 | return &exec.Cmd{ 17 | Path: path, 18 | Args: []string{"go", "mod", "graph"}, 19 | Dir: workingDirectory, 20 | }, err 21 | } 22 | 23 | func (_ CmdFactory) MakeListCmd(workingDirectory string) (*exec.Cmd, error) { 24 | path, err := exec.LookPath("go") 25 | 26 | return &exec.Cmd{ 27 | Path: path, 28 | Args: []string{"go", "list", "-mod=readonly", "-e", "-m", "all"}, 29 | Dir: workingDirectory, 30 | }, err 31 | } 32 | 33 | func (_ CmdFactory) MakeListJsonCmd(workingDirectory string) (*exec.Cmd, error) { 34 | path, err := exec.LookPath("go") 35 | 36 | return &exec.Cmd{ 37 | Path: path, 38 | Args: []string{path, "list", "-json", "./..."}, 39 | Dir: workingDirectory, 40 | }, err 41 | } 42 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeGraphCmd(t *testing.T) { 10 | cmd, _ := CmdFactory{}.MakeGraphCmd(".") 11 | assert.NotNil(t, cmd) 12 | args := cmd.Args 13 | assert.Contains(t, args, "go") 14 | assert.Contains(t, args, "mod") 15 | assert.Contains(t, args, "graph") 16 | } 17 | 18 | func TestMakeListCmd(t *testing.T) { 19 | cmd, _ := CmdFactory{}.MakeListCmd(".") 20 | assert.NotNil(t, cmd) 21 | args := cmd.Args 22 | assert.Contains(t, args, "go") 23 | assert.Contains(t, args, "list") 24 | assert.Contains(t, args, "-mod=readonly") 25 | assert.Contains(t, args, "-e") 26 | assert.Contains(t, args, "-m") 27 | assert.Contains(t, args, "all") 28 | } 29 | 30 | func TestMakeListJsonCmd(t *testing.T) { 31 | factory := CmdFactory{} 32 | cmd, err := factory.MakeListJsonCmd(".") 33 | assert.Nil(t, err) 34 | assert.NotNil(t, cmd) 35 | assert.Contains(t, cmd.Args, "list") 36 | assert.Contains(t, cmd.Args, "-json") 37 | assert.Contains(t, cmd.Args, "./...") 38 | } 39 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/pm.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | const Name = "go" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (_ Pm) Manifests() []string { 20 | return []string{ 21 | "go.mod", 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/pm_test.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewPm(t *testing.T) { 10 | pm := NewPm() 11 | assert.Equal(t, Name, pm.name) 12 | } 13 | 14 | func TestName(t *testing.T) { 15 | pm := NewPm() 16 | assert.Equal(t, Name, pm.Name()) 17 | } 18 | 19 | func TestManifests(t *testing.T) { 20 | pm := Pm{} 21 | manifests := pm.Manifests() 22 | assert.Len(t, manifests, 1) 23 | manifest := manifests[0] 24 | assert.Equal(t, "go.mod", manifest) 25 | } 26 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/strategy.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | "github.com/debricked/cli/internal/resolution/pm/writer" 6 | ) 7 | 8 | type Strategy struct { 9 | files []string 10 | } 11 | 12 | func (s Strategy) Invoke() ([]job.IJob, error) { 13 | var jobs []job.IJob 14 | for _, file := range s.files { 15 | jobs = append(jobs, NewJob(file, CmdFactory{}, writer.FileWriter{})) 16 | } 17 | 18 | return jobs, nil 19 | } 20 | 21 | func NewStrategy(files []string) Strategy { 22 | return Strategy{files} 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/gomod/strategy_test.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/README.md: -------------------------------------------------------------------------------- 1 | # Gradle resolution logic 2 | 3 | The way resolution of gradle lock files works is as follows: 4 | 5 | 1. Generate init script file for project and subprojects 6 | 2. Run `gradle --init-script gradle-init-script.groovy debrickedAllDeps` in order to create dependencies graph 7 | 3. In case permission to execute gradlew is not granted, fallback to PATHs gradle installation is used: `gradle --init-script gradle-init-script.groovy debrickedFindSubProjectPaths` 8 | 9 | The results of the executed command above is then being written into the lock file. 10 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type ICmdFactory interface { 8 | MakeFindSubGraphCmd(workingDirectory string, gradlew string, initScript string) (*exec.Cmd, error) 9 | MakeDependenciesGraphCmd(workingDirectory string, gradlew string, initScript string) (*exec.Cmd, error) 10 | } 11 | 12 | type CmdFactory struct{} 13 | 14 | func (cf CmdFactory) MakeFindSubGraphCmd(workingDirectory string, gradlew string, initScript string) (*exec.Cmd, error) { 15 | path, err := exec.LookPath(gradlew) 16 | 17 | return &exec.Cmd{ 18 | Path: path, 19 | Args: []string{gradlew, "--init-script", initScript, "debrickedFindSubProjectPaths"}, 20 | Dir: workingDirectory, 21 | }, err 22 | } 23 | 24 | func (cf CmdFactory) MakeDependenciesGraphCmd(workingDirectory string, gradlew string, initScript string) (*exec.Cmd, error) { 25 | path, err := exec.LookPath(gradlew) 26 | 27 | return &exec.Cmd{ 28 | Path: path, 29 | Args: []string{gradlew, "--init-script", initScript, "debrickedAllDeps"}, 30 | Dir: workingDirectory, 31 | }, err 32 | } 33 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeFindSubGraphCmd(t *testing.T) { 10 | cmd, _ := CmdFactory{}.MakeFindSubGraphCmd(".", "gradlew", "init.gradle") 11 | assert.NotNil(t, cmd) 12 | args := cmd.Args 13 | assert.Contains(t, args, "gradlew") 14 | assert.Contains(t, args, "--init-script") 15 | assert.Contains(t, args, "init.gradle") 16 | assert.Contains(t, args, "debrickedFindSubProjectPaths") 17 | } 18 | 19 | func TestMakeDependenciesGraphCmd(t *testing.T) { 20 | cmd, _ := CmdFactory{}.MakeDependenciesGraphCmd(".", "gradlew", "init.gradle") 21 | assert.NotNil(t, cmd) 22 | args := cmd.Args 23 | assert.Contains(t, args, "gradlew") 24 | assert.Contains(t, args, "--init-script") 25 | assert.Contains(t, args, "init.gradle") 26 | assert.Contains(t, args, "debrickedAllDeps") 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/gradle-init/gradle-init-script.groovy: -------------------------------------------------------------------------------- 1 | def debrickedOutputFile = new File('.debricked.multiprojects.txt') 2 | 3 | allprojects { 4 | task debrickedFindSubProjectPaths() { 5 | String output = project.projectDir 6 | doLast { 7 | synchronized(debrickedOutputFile) { 8 | debrickedOutputFile << output + System.getProperty("line.separator") 9 | } 10 | } 11 | } 12 | } 13 | 14 | allprojects { 15 | task debrickedAllDeps(type: DependencyReportTask) { 16 | outputFile = file('./gradle.debricked.lock') 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/init_script_handler.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/pm/writer" 5 | ) 6 | 7 | type IInitScriptHandler interface { 8 | ReadInitFile() ([]byte, error) 9 | WriteInitFile(targetFileName string, fileWriter writer.IFileWriter) error 10 | } 11 | 12 | type InitScriptHandler struct{} 13 | 14 | func (_ InitScriptHandler) ReadInitFile() ([]byte, error) { 15 | return gradleInitScript.ReadFile("gradle-init/gradle-init-script.groovy") 16 | } 17 | 18 | func (i InitScriptHandler) WriteInitFile(targetFileName string, fileWriter writer.IFileWriter) error { 19 | content, err := i.ReadInitFile() 20 | if err != nil { 21 | 22 | return SetupScriptError{message: err.Error()} 23 | } 24 | lockFile, err := fileWriter.Create(targetFileName) 25 | if err != nil { 26 | 27 | return SetupScriptError{message: err.Error()} 28 | } 29 | defer lockFile.Close() 30 | err = fileWriter.Write(lockFile, content) 31 | if err != nil { 32 | 33 | return SetupScriptError{message: err.Error()} 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/init_script_handler_test.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | import ( 4 | "embed" 5 | "errors" 6 | "testing" 7 | 8 | writerTestdata "github.com/debricked/cli/internal/resolution/pm/writer/testdata" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestWriteInitFile(t *testing.T) { 13 | createErr := errors.New("create-error") 14 | fileWriterMock := &writerTestdata.FileWriterMock{CreateErr: createErr} 15 | 16 | sf := InitScriptHandler{} 17 | err := sf.WriteInitFile("file", fileWriterMock) 18 | assert.Equal(t, SetupScriptError{createErr.Error()}, err) 19 | 20 | fileWriterMock = &writerTestdata.FileWriterMock{WriteErr: createErr} 21 | err = sf.WriteInitFile("file", fileWriterMock) 22 | assert.Equal(t, SetupScriptError{createErr.Error()}, err) 23 | } 24 | 25 | func TestWriteInitFileNoInitFile(t *testing.T) { 26 | sf := InitScriptHandler{} 27 | oldGradleInitScript := gradleInitScript 28 | defer func() { 29 | gradleInitScript = oldGradleInitScript 30 | }() 31 | gradleInitScript = embed.FS{} 32 | err := sf.WriteInitFile("file", nil) 33 | readErr := errors.New("open gradle-init/gradle-init-script.groovy: file does not exist") 34 | assert.Equal(t, SetupScriptError{readErr.Error()}, err) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/pm.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | const Name = "gradle" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (_ Pm) Manifests() []string { 20 | return []string{ 21 | "build.gradle", 22 | "build.gradle.kts", 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/pm_test.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewPm(t *testing.T) { 10 | pm := NewPm() 11 | assert.Equal(t, Name, pm.name) 12 | } 13 | 14 | func TestName(t *testing.T) { 15 | pm := NewPm() 16 | assert.Equal(t, Name, pm.Name()) 17 | } 18 | 19 | func TestManifests(t *testing.T) { 20 | pm := Pm{} 21 | manifests := pm.Manifests() 22 | assert.Len(t, manifests, 2) 23 | manifest := manifests[0] 24 | assert.Equal(t, "build.gradle", manifest) 25 | } 26 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/project.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | type Project struct { 4 | dir string 5 | gradlew string 6 | mainBuildFile string 7 | } 8 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/setup_err.go: -------------------------------------------------------------------------------- 1 | package gradle 2 | 3 | type SetupScriptError struct { 4 | message string 5 | } 6 | 7 | type SetupWalkError struct { 8 | message string 9 | } 10 | 11 | type SetupSubprojectError struct { 12 | message string 13 | } 14 | 15 | func (e SetupScriptError) Error() string { 16 | 17 | return e.message 18 | } 19 | 20 | func (e SetupWalkError) Error() string { 21 | 22 | return e.message 23 | } 24 | 25 | func (e SetupSubprojectError) Error() string { 26 | 27 | return e.message 28 | } 29 | 30 | type SetupError []error 31 | 32 | func (e SetupError) Error() string { 33 | var s string 34 | for _, err := range e { 35 | s += err.Error() + "\n" 36 | } 37 | 38 | return s 39 | } 40 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | "strings" 6 | ) 7 | 8 | type CmdFactoryMock struct { 9 | Err error 10 | Name string 11 | } 12 | 13 | func (f CmdFactoryMock) MakeDependenciesGraphCmd(dir string, gradlew string, _ string) (*exec.Cmd, error) { 14 | err := f.Err 15 | if gradlew == "gradle" { 16 | err = nil 17 | } 18 | 19 | if f.Err != nil && strings.HasPrefix(f.Err.Error(), "give-error-on-gradle") { 20 | err = f.Err 21 | } 22 | 23 | return exec.Command(f.Name, `MakeDependenciesCmd`), err 24 | } 25 | 26 | // implement the interface 27 | func (f CmdFactoryMock) MakeFindSubGraphCmd(_ string, _ string, _ string) (*exec.Cmd, error) { 28 | return exec.Command(f.Name, `MakeFindSubGraphCmd`), f.Err 29 | } 30 | 31 | // implement the interface 32 | func (f CmdFactoryMock) MakeDependenciesCmd(_ string) (*exec.Cmd, error) { 33 | return exec.Command(f.Name, `MakeDependenciesCmd`), f.Err 34 | } 35 | -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/project/build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/resolution/pm/gradle/testdata/project/build.gradle -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/project/gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/resolution/pm/gradle/testdata/project/gradlew -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/project/gradlew.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/resolution/pm/gradle/testdata/project/gradlew.bat -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/project/settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/resolution/pm/gradle/testdata/project/settings.gradle -------------------------------------------------------------------------------- /internal/resolution/pm/gradle/testdata/project/subproject/build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/resolution/pm/gradle/testdata/project/subproject/build.gradle -------------------------------------------------------------------------------- /internal/resolution/pm/maven/README.md: -------------------------------------------------------------------------------- 1 | # Maven resolution logic 2 | 3 | The way resolution of maven lock files works is as follows: 4 | 5 | 1. Parse `pom.xml` file 6 | 2. Run `mvn dependency:tree -DoutputFile=maven.debricked.lock -DoutputType=tgf --fail-at-end` in order to install all dependencies 7 | 8 | The result of the second command above is then written to `maven.debricked.lock` file. 9 | 10 | ## Private dependencies / Third party repositories 11 | 12 | Many maven projects use repositories other than the default central repository, this can be configured in the projects pom.xml. 13 | However, if the repository is not public it could require authentication and some configuration may be needed for the Debricked CLI to be able to resolve dependencies. 14 | 15 | In general, the authentication is handled in a file called `settings.xml` in the `.m2` folder (see [settings documentation](https://maven.apache.org/settings.html) for more information). On your build server and locally on development machines this is probably 16 | already setup, but that may not be the case for the environment where the Debricked scan is running, meaning it will fail to resolve. 17 | To fix this the settings file can be manually changed to add the configuration for the required private repositories, or it can be configured by the pipeline provider (such as AWS or Azure). 18 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import "os/exec" 4 | 5 | type ICmdFactory interface { 6 | MakeDependencyTreeCmd(workingDirectory string) (*exec.Cmd, error) 7 | } 8 | 9 | type CmdFactory struct{} 10 | 11 | func (_ CmdFactory) MakeDependencyTreeCmd(workingDirectory string) (*exec.Cmd, error) { 12 | path, err := exec.LookPath("mvn") 13 | 14 | return &exec.Cmd{ 15 | Path: path, 16 | Args: []string{ 17 | "mvn", 18 | "dependency:tree", 19 | "-DoutputFile=" + lockFileExtension, 20 | "-DoutputType=tgf", 21 | "--fail-at-end", 22 | }, 23 | Dir: workingDirectory, 24 | }, err 25 | } 26 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeDependencyTreeCmd(t *testing.T) { 10 | cmd, _ := CmdFactory{}.MakeDependencyTreeCmd(".") 11 | assert.NotNil(t, cmd) 12 | args := cmd.Args 13 | assert.Contains(t, args, "mvn") 14 | assert.Contains(t, args, "dependency:tree") 15 | assert.Contains(t, args, "-DoutputFile=maven.debricked.lock") 16 | assert.Contains(t, args, "-DoutputType=tgf") 17 | assert.Contains(t, args, "--fail-at-end") 18 | } 19 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/pm.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | const Name = "mvn" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (_ Pm) Manifests() []string { 20 | return []string{ 21 | "pom.xml", 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/pm_test.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewPm(t *testing.T) { 10 | pm := NewPm() 11 | assert.Equal(t, Name, pm.name) 12 | } 13 | 14 | func TestName(t *testing.T) { 15 | pm := NewPm() 16 | assert.Equal(t, Name, pm.Name()) 17 | } 18 | 19 | func TestManifests(t *testing.T) { 20 | pm := Pm{} 21 | manifests := pm.Manifests() 22 | assert.Len(t, manifests, 1) 23 | manifest := manifests[0] 24 | assert.Equal(t, "pom.xml", manifest) 25 | } 26 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/pom_service.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "github.com/vifraa/gopom" 5 | ) 6 | 7 | type IPomService interface { 8 | ParsePomModules(path string) ([]string, error) 9 | } 10 | 11 | type PomService struct{} 12 | 13 | func (p PomService) ParsePomModules(path string) ([]string, error) { 14 | pom, err := gopom.Parse(path) 15 | 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return pom.Modules, nil 21 | } 22 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/pom_service_test.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParsePomModules(t *testing.T) { 10 | p := PomService{} 11 | modules, err := p.ParsePomModules("testdata/pom.xml") 12 | assert.Nil(t, err) 13 | assert.Len(t, modules, 5) 14 | correct := []string{"guava", "guava-bom", "guava-gwt", "guava-testlib", "guava-tests"} 15 | assert.Equal(t, correct, modules) 16 | 17 | modules, err = p.ParsePomModules("testdata/notAPom.xml") 18 | 19 | assert.NotNil(t, err) 20 | assert.Len(t, modules, 0) 21 | } 22 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/strategy.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type Strategy struct { 8 | files []string 9 | cmdFactory ICmdFactory 10 | } 11 | 12 | func NewStrategy(files []string) Strategy { 13 | return Strategy{files, CmdFactory{}} 14 | } 15 | 16 | func (s Strategy) Invoke() ([]job.IJob, error) { 17 | var jobs []job.IJob 18 | 19 | for _, file := range s.files { 20 | jobs = append(jobs, NewJob(file, s.cmdFactory, PomService{})) 21 | } 22 | 23 | return jobs, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/strategy_test.go: -------------------------------------------------------------------------------- 1 | package maven 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type PomServiceMock struct{} 10 | 11 | func (p PomServiceMock) GetRootPomFiles(files []string) []string { 12 | return files 13 | } 14 | 15 | func (p PomServiceMock) ParsePomModules(_ string) ([]string, error) { 16 | return []string{}, nil 17 | } 18 | 19 | func TestNewStrategy(t *testing.T) { 20 | s := NewStrategy(nil) 21 | assert.NotNil(t, s) 22 | assert.Len(t, s.files, 0) 23 | 24 | s = NewStrategy([]string{}) 25 | assert.NotNil(t, s) 26 | assert.Len(t, s.files, 0) 27 | 28 | s = NewStrategy([]string{"file"}) 29 | assert.NotNil(t, s) 30 | assert.Len(t, s.files, 1) 31 | 32 | s = NewStrategy([]string{"file-1", "file-2"}) 33 | assert.NotNil(t, s) 34 | assert.Len(t, s.files, 2) 35 | } 36 | 37 | func TestInvokeNoFiles(t *testing.T) { 38 | s := NewStrategy([]string{}) 39 | 40 | jobs, _ := s.Invoke() 41 | 42 | assert.Empty(t, jobs) 43 | } 44 | 45 | func TestInvokeOneFile(t *testing.T) { 46 | s := NewStrategy([]string{"file"}) 47 | 48 | jobs, _ := s.Invoke() 49 | 50 | assert.Len(t, jobs, 1) 51 | } 52 | 53 | func TestInvokeManyFiles(t *testing.T) { 54 | s := NewStrategy([]string{"file-1", "file-2"}) 55 | 56 | jobs, _ := s.Invoke() 57 | 58 | assert.Len(t, jobs, 2) 59 | } 60 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | "runtime" 6 | ) 7 | 8 | type CmdFactoryMock struct { 9 | Err error 10 | Name string 11 | Arg string 12 | } 13 | 14 | func (f CmdFactoryMock) MakeDependencyTreeCmd(_ string) (*exec.Cmd, error) { 15 | if len(f.Arg) == 0 { 16 | f.Arg = `"MakeDependencyTreeCmd"` 17 | } 18 | 19 | if runtime.GOOS == "windows" && f.Name == "echo" { 20 | return exec.Command("cmd", "/C", f.Name, f.Arg), nil 21 | } 22 | 23 | return exec.Command(f.Name, f.Arg), f.Err 24 | } 25 | -------------------------------------------------------------------------------- /internal/resolution/pm/maven/testdata/notAPom.xml: -------------------------------------------------------------------------------- 1 | pandas==1.1.1 2 | # comment 3 | numpy==1.2.3 -------------------------------------------------------------------------------- /internal/resolution/pm/maven/testdata/pom_service_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type PomServiceMock struct { 4 | Value []string 5 | Err error 6 | } 7 | 8 | func (p PomServiceMock) ParsePomModules(_ string) ([]string, error) { 9 | if p.Err != nil { 10 | return nil, p.Err 11 | } 12 | 13 | return p.Value, nil 14 | } 15 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/README.md: -------------------------------------------------------------------------------- 1 | # NPM resolution logic 2 | 3 | The way resolution of NPM lock files works is as follows: 4 | 5 | 1. Run `npm install --ignore-scripts --audit=false --bin-links=false` in order to install all dependencies 6 | 7 | Generated `package-lock.json` file is then uploaded together with `package.json` for scanning. 8 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "os/exec" 5 | "path/filepath" 6 | ) 7 | 8 | type ICmdFactory interface { 9 | MakeInstallCmd(command string, file string) (*exec.Cmd, error) 10 | } 11 | 12 | type IExecPath interface { 13 | LookPath(file string) (string, error) 14 | } 15 | 16 | type ExecPath struct { 17 | } 18 | 19 | func (ExecPath) LookPath(file string) (string, error) { 20 | return exec.LookPath(file) 21 | } 22 | 23 | type CmdFactory struct { 24 | execPath IExecPath 25 | } 26 | 27 | func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 28 | path, err := cmdf.execPath.LookPath(command) 29 | 30 | fileDir := filepath.Dir(file) 31 | 32 | return &exec.Cmd{ 33 | Path: path, 34 | Args: []string{ 35 | //"yes |", // Answer 'y' to any prompts... 36 | command, 37 | "install", 38 | "--ignore-scripts", // Avoid risky scripts 39 | "--audit=false", // Do not run audit 40 | "--bin-links=false", // We don't need symlinks to binaries as we won't run any code 41 | }, 42 | Dir: fileDir, 43 | }, err 44 | } 45 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeInstallCmd(t *testing.T) { 10 | npmCommand := "npm" 11 | cmd, err := CmdFactory{ 12 | execPath: ExecPath{}, 13 | }.MakeInstallCmd(npmCommand, "file") 14 | assert.NoError(t, err) 15 | assert.NotNil(t, cmd) 16 | args := cmd.Args 17 | assert.Contains(t, args, "npm") 18 | assert.Contains(t, args, "install") 19 | } 20 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/pm.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | const Name = "npm" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (Pm) Manifests() []string { 20 | return []string{ 21 | `package\.json$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/pm_test.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 1) 24 | manifest := manifests[0] 25 | assert.Equal(t, `package\.json$`, manifest) 26 | _, err := regexp.Compile(manifest) 27 | assert.NoError(t, err) 28 | 29 | cases := map[string]bool{ 30 | "package.json": true, 31 | "package-lock.json": false, 32 | "npm.lock": false, 33 | } 34 | for file, isMatch := range cases { 35 | t.Run(file, func(t *testing.T) { 36 | matched, _ := regexp.MatchString(manifest, file) 37 | assert.Equal(t, isMatch, matched) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/strategy.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type Strategy struct { 8 | files []string 9 | } 10 | 11 | func (s Strategy) Invoke() ([]job.IJob, error) { 12 | var jobs []job.IJob 13 | for _, file := range s.files { 14 | jobs = append(jobs, NewJob( 15 | file, 16 | true, 17 | CmdFactory{ 18 | execPath: ExecPath{}, 19 | }, 20 | ), 21 | ) 22 | } 23 | 24 | return jobs, nil 25 | } 26 | 27 | func NewStrategy(files []string) Strategy { 28 | return Strategy{files} 29 | } 30 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/strategy_test.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/npm/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type CmdFactoryMock struct { 8 | InstallCmdName string 9 | MakeInstallErr error 10 | } 11 | 12 | func NewEchoCmdFactory() CmdFactoryMock { 13 | return CmdFactoryMock{ 14 | InstallCmdName: "echo", 15 | } 16 | } 17 | 18 | func (f CmdFactoryMock) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 19 | return exec.Command(f.InstallCmdName), f.MakeInstallErr 20 | } 21 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/README.md: -------------------------------------------------------------------------------- 1 | # Nuget resolution logic 2 | 3 | There are two supported files for resolution of nuget lock files: 4 | 5 | ### packages.config 6 | 7 | We need to convert a `packages.config` file to a `.csproj` file. This is to enable the use of the dotnet restore command 8 | that enables Debricked to parse out transitive dependencies. This may add some additional framework dependencies that 9 | will not show up if we only scan the `packages.config` file. This is done in a few steps: 10 | 11 | 1. Parse `packages.config` file 12 | 2. Run `dotnet --version` to get dotnet version 13 | 3. Collect unique target frameworks and packages from the file 14 | 4. Create `.nuget.debricked.csproj.temp` file with the collected data 15 | 16 | With this done we can move on to the next section 17 | 18 | ### .csproj 19 | 20 | 1. Run `dotnet restore --use-lock-file --lock-file-path ` in order to restore the dependencies and tools of a project (lock file name can be different depend on which manifest file is being resolved) 21 | 2. Cleanup temporary csproj file after lock file is created (for `packages.config` case) 22 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/pm.go: -------------------------------------------------------------------------------- 1 | package nuget 2 | 3 | const Name = "nuget" 4 | const CsprojRegex = `\.csproj$` 5 | const PackagesConfigRegex = `packages\.config$` 6 | 7 | type Pm struct { 8 | name string 9 | } 10 | 11 | func NewPm() Pm { 12 | return Pm{ 13 | name: Name, 14 | } 15 | } 16 | 17 | func (pm Pm) Name() string { 18 | return pm.name 19 | } 20 | 21 | func (Pm) Manifests() []string { 22 | return []string{ 23 | CsprojRegex, 24 | PackagesConfigRegex, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/pm_test.go: -------------------------------------------------------------------------------- 1 | package nuget 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 2) 24 | manifestCs := manifests[0] 25 | assert.Equal(t, `\.csproj$`, manifestCs) 26 | _, err := regexp.Compile(manifestCs) 27 | assert.NoError(t, err) 28 | 29 | manifestPc := manifests[1] 30 | assert.Equal(t, `packages\.config$`, manifestPc) 31 | _, err = regexp.Compile(manifestPc) 32 | assert.NoError(t, err) 33 | 34 | cases := map[string]bool{ 35 | "test.csproj": true, 36 | "sample3.csproj": true, 37 | ".csproj": true, 38 | "test.csproj.user": false, 39 | "test.csproj.nuget": false, 40 | "test.csproj.nuget.props": false, 41 | "package.json.lock": false, 42 | "packages.config": true, 43 | } 44 | for file, isMatch := range cases { 45 | t.Run(file, func(t *testing.T) { 46 | 47 | matchedCs, _ := regexp.MatchString(manifestCs, file) 48 | matchedPc, _ := regexp.MatchString(manifestPc, file) 49 | matched := matchedCs || matchedPc 50 | 51 | assert.Equal(t, isMatch, matched, "file: %s", file) 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/strategy.go: -------------------------------------------------------------------------------- 1 | package nuget 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type Strategy struct { 8 | files []string 9 | } 10 | 11 | func (s Strategy) Invoke() ([]job.IJob, error) { 12 | var jobs []job.IJob 13 | for _, file := range s.files { 14 | jobs = append(jobs, NewJob( 15 | file, 16 | true, 17 | NewCmdFactory(ExecPath{}), 18 | ), 19 | ) 20 | } 21 | 22 | return jobs, nil 23 | } 24 | 25 | func NewStrategy(files []string) Strategy { 26 | return Strategy{files} 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/strategy_test.go: -------------------------------------------------------------------------------- 1 | package nuget 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type CmdFactoryMock struct { 8 | InstallCmdName string 9 | MakeInstallErr error 10 | GetTempoCsprojReturn string 11 | } 12 | 13 | func NewEchoCmdFactory() CmdFactoryMock { 14 | return CmdFactoryMock{ 15 | InstallCmdName: "echo", 16 | GetTempoCsprojReturn: "", 17 | } 18 | } 19 | 20 | func (f CmdFactoryMock) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 21 | return exec.Command(f.InstallCmdName), f.MakeInstallErr 22 | } 23 | 24 | func (f CmdFactoryMock) GetTempoCsproj() string { 25 | return f.GetTempoCsprojReturn 26 | } 27 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/empty_cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type EmptyCmdFactoryMock struct { 8 | MakeErr error 9 | } 10 | 11 | func NewEmptyCmdFactory() EmptyCmdFactoryMock { 12 | return EmptyCmdFactoryMock{} 13 | } 14 | 15 | func (f EmptyCmdFactoryMock) MakeInstallCmd(_ string, _ string) (*exec.Cmd, error) { 16 | return nil, f.MakeErr 17 | } 18 | 19 | func (f EmptyCmdFactoryMock) GetTempoCsproj() string { 20 | return "" 21 | } 22 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/invalid/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/invalid_dependency/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/missing_framework/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/missing_framework/packages.config.nuget.debricked.csproj.temp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /internal/resolution/pm/nuget/testdata/valid/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/README.md: -------------------------------------------------------------------------------- 1 | # Pip resolution logic 2 | 3 | The way resolution of pip lock files works is as follows: 4 | 5 | 1. Create a Venv in which we do the installation and run all commands 6 | 2. Run `pip install -r ` in order to install all dependencies 7 | 3. Run `cat` to get the contents of the requirements.txt file 8 | 4. Run `pip list` to get a list of all installed packages 9 | 5. Run `pip show ` to get more in-depth information from each package, including the relations between dependencies 10 | 11 | The results of the commands above are then combined to form the finished lock file with the following sections: 12 | 13 | 1. The contents of the requirements.txt (from cat) 14 | 2. The list of all installed dependencies (from pip list) 15 | 3. More detailed information on each package with relations (from pip show) 16 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/pm.go: -------------------------------------------------------------------------------- 1 | package pip 2 | 3 | const Name = "pip" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (_ Pm) Manifests() []string { 20 | return []string{ 21 | `requirements.*\.txt$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/strategy.go: -------------------------------------------------------------------------------- 1 | package pip 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | "github.com/debricked/cli/internal/resolution/pm/writer" 6 | ) 7 | 8 | type Strategy struct { 9 | files []string 10 | } 11 | 12 | func (s Strategy) Invoke() ([]job.IJob, error) { 13 | var jobs []job.IJob 14 | for _, file := range s.files { 15 | jobs = append(jobs, NewJob( 16 | file, 17 | true, 18 | CmdFactory{ 19 | execPath: ExecPath{}, 20 | }, 21 | writer.FileWriter{}, 22 | pipCleaner{}, 23 | ), 24 | ) 25 | } 26 | 27 | return jobs, nil 28 | } 29 | 30 | func NewStrategy(files []string) Strategy { 31 | return Strategy{files} 32 | } 33 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/strategy_test.go: -------------------------------------------------------------------------------- 1 | package pip 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/testdata/list.txt: -------------------------------------------------------------------------------- 1 | Package Version Editable project location 2 | ----------------------------- ------------ ------------------------------------------------------ 3 | aiohttp 3.7.4 4 | cryptography 3.4.7 5 | numpy 1.23.4 6 | Flask 2.0.3 7 | open-source-health 0.1 /path/to/folder 8 | pandas 1.4.3 9 | tqdm 4.63.0 -------------------------------------------------------------------------------- /internal/resolution/pm/pip/testdata/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.1.5 2 | sentry-sdk==1.5.4 3 | sentry-sdk[flask] 4 | 5 | pandas>=1.4.0 6 | # matplotlib 7 | # seaborn 8 | tqdm 9 | 10 | 11 | cryptography>=3.3.2,<4.0.0 12 | # test 13 | -------------------------------------------------------------------------------- /internal/resolution/pm/pip/testdata/show.txt: -------------------------------------------------------------------------------- 1 | Name: Flask 2 | Version: 2.1.2 3 | Summary: A simple framework for building complex web applications. 4 | Home-page: https://palletsprojects.com/p/flask 5 | Author: Armin Ronacher 6 | Author-email: armin.ronacher@active-4.com 7 | License: BSD-3-Clause 8 | Location: /path/to/site-packages 9 | Requires: click, importlib-metadata, itsdangerous, Jinja2, Werkzeug 10 | Required-by: Flask-Script, Flask-Compress, Flask-Bcrypt 11 | --- 12 | Name: tqdm 13 | Version: 4.64.0 14 | Summary: Fast, Extensible Progress Meter 15 | Home-page: https://tqdm.github.io 16 | Author: 17 | Author-email: 18 | License: MPLv2.0, MIT Licences 19 | Location: /path/to/site-packages 20 | Requires: 21 | Required-by: transformers, nltk 22 | --- 23 | Name: pandas 24 | Version: 1.4.2 25 | Summary: Powerful data structures for data analysis, time series, and statistics 26 | Home-page: https://pandas.pydata.org 27 | Author: The Pandas Development Team 28 | Author-email: pandas-dev@python.org 29 | License: BSD-3-Clause 30 | Location: /path/to/site-packages 31 | Requires: python-dateutil, pytz, numpy 32 | Required-by: xarray, seaborn, hvplot, holoviews 33 | --- 34 | Name: numpy 35 | Version: 1.21.5 36 | Summary: NumPy is the fundamental package for array computing with Python. 37 | Home-page: https://www.numpy.org 38 | Author: Travis E. Oliphant et al. 39 | Author-email: 40 | License: BSD 41 | Location: /path/to/site-packages 42 | Requires: 43 | Required-by: xarray, transformers, pandas, astropy 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/pm.go: -------------------------------------------------------------------------------- 1 | package pm 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/pm/bower" 5 | "github.com/debricked/cli/internal/resolution/pm/composer" 6 | "github.com/debricked/cli/internal/resolution/pm/gomod" 7 | "github.com/debricked/cli/internal/resolution/pm/gradle" 8 | "github.com/debricked/cli/internal/resolution/pm/maven" 9 | "github.com/debricked/cli/internal/resolution/pm/npm" 10 | "github.com/debricked/cli/internal/resolution/pm/nuget" 11 | "github.com/debricked/cli/internal/resolution/pm/pip" 12 | "github.com/debricked/cli/internal/resolution/pm/sbt" 13 | "github.com/debricked/cli/internal/resolution/pm/yarn" 14 | ) 15 | 16 | type IPm interface { 17 | Name() string 18 | Manifests() []string 19 | } 20 | 21 | func Pms() []IPm { 22 | return []IPm{ 23 | maven.NewPm(), 24 | gradle.NewPm(), 25 | gomod.NewPm(), 26 | pip.NewPm(), 27 | yarn.NewPm(), 28 | npm.NewPm(), 29 | bower.NewPm(), 30 | nuget.NewPm(), 31 | composer.NewPm(), 32 | sbt.NewPm(), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/resolution/pm/pm_test.go: -------------------------------------------------------------------------------- 1 | package pm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPms(t *testing.T) { 10 | pms := Pms() 11 | pmNames := []string{ 12 | "mvn", 13 | "go", 14 | "gradle", 15 | "composer", 16 | } 17 | 18 | for _, pmName := range pmNames { 19 | t.Run(pmName, func(t *testing.T) { 20 | contains := false 21 | for _, pm := range pms { 22 | contains = contains || pm.Name() == pmName 23 | } 24 | assert.Truef(t, contains, "failed to assert that %s was returned in Pms()", pmName) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import "os/exec" 4 | 5 | type ICmdFactory interface { 6 | MakePomCmd(workingDirectory string) (*exec.Cmd, error) 7 | } 8 | 9 | type CmdFactory struct{} 10 | 11 | func (CmdFactory) MakePomCmd(workingDirectory string) (*exec.Cmd, error) { 12 | path, err := exec.LookPath("sbt") 13 | 14 | return &exec.Cmd{ 15 | Path: path, 16 | Args: []string{ 17 | "sbt", 18 | "makePom", 19 | }, 20 | Dir: workingDirectory, 21 | }, err 22 | } 23 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakePomCmd(t *testing.T) { 10 | cmd, _ := CmdFactory{}.MakePomCmd(".") 11 | assert.NotNil(t, cmd) 12 | args := cmd.Args 13 | assert.Contains(t, args, "sbt") 14 | assert.Contains(t, args, "makePom") 15 | } 16 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/pm.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | const Name = "sbt" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (Pm) Manifests() []string { 20 | return []string{ 21 | `^build\.sbt$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/pm_test.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 1) 24 | manifest := manifests[0] 25 | assert.Equal(t, `^build\.sbt$`, manifest) 26 | _, err := regexp.Compile(manifest) 27 | assert.NoError(t, err) 28 | 29 | cases := map[string]bool{ 30 | "build.sbt": true, 31 | "BUILD.sbt": false, 32 | "build.sbt.backup": false, 33 | "mybuild.sbt": false, 34 | "build.scala": false, 35 | "pom.xml": false, 36 | } 37 | for file, isMatch := range cases { 38 | t.Run(file, func(t *testing.T) { 39 | matched, _ := regexp.MatchString(manifest, file) 40 | assert.Equal(t, isMatch, matched) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/strategy.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | "github.com/debricked/cli/internal/resolution/pm/maven" 6 | ) 7 | 8 | type Strategy struct { 9 | files []string 10 | cmdFactory ICmdFactory 11 | buildService IBuildService 12 | mavenPomService maven.IPomService 13 | mavenCmdFactory maven.ICmdFactory 14 | } 15 | 16 | func NewStrategy(files []string) Strategy { 17 | return Strategy{ 18 | files: files, 19 | cmdFactory: CmdFactory{}, 20 | buildService: BuildService{}, 21 | mavenPomService: maven.PomService{}, 22 | mavenCmdFactory: maven.CmdFactory{}, 23 | } 24 | } 25 | 26 | func (s Strategy) Invoke() ([]job.IJob, error) { 27 | var jobs []job.IJob 28 | 29 | for _, file := range s.files { 30 | jobs = append(jobs, NewJob(file, s.cmdFactory, s.buildService, s.mavenPomService, s.mavenCmdFactory)) 31 | } 32 | 33 | return jobs, nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/strategy_test.go: -------------------------------------------------------------------------------- 1 | package sbt 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type BuildServiceMock struct{} 10 | 11 | func (b BuildServiceMock) ParseBuildModules(_ string) ([]string, error) { 12 | return []string{}, nil 13 | } 14 | 15 | func TestNewStrategy(t *testing.T) { 16 | s := NewStrategy(nil) 17 | assert.NotNil(t, s) 18 | assert.Len(t, s.files, 0) 19 | 20 | s = NewStrategy([]string{}) 21 | assert.NotNil(t, s) 22 | assert.Len(t, s.files, 0) 23 | 24 | s = NewStrategy([]string{"file"}) 25 | assert.NotNil(t, s) 26 | assert.Len(t, s.files, 1) 27 | 28 | s = NewStrategy([]string{"file-1", "file-2"}) 29 | assert.NotNil(t, s) 30 | assert.Len(t, s.files, 2) 31 | } 32 | 33 | func TestInvokeNoFiles(t *testing.T) { 34 | s := NewStrategy([]string{}) 35 | 36 | jobs, _ := s.Invoke() 37 | 38 | assert.Empty(t, jobs) 39 | } 40 | 41 | func TestInvokeOneFile(t *testing.T) { 42 | s := NewStrategy([]string{"file"}) 43 | 44 | jobs, _ := s.Invoke() 45 | 46 | assert.Len(t, jobs, 1) 47 | } 48 | 49 | func TestInvokeManyFiles(t *testing.T) { 50 | s := NewStrategy([]string{"file-1", "file-2"}) 51 | 52 | jobs, _ := s.Invoke() 53 | 54 | assert.Len(t, jobs, 2) 55 | } 56 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/testdata/build_service_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type BuildServiceMock struct { 4 | Value []string 5 | Err error 6 | } 7 | 8 | func (b BuildServiceMock) ParseBuildModules(_ string) ([]string, error) { 9 | if b.Err != nil { 10 | return nil, b.Err 11 | } 12 | 13 | if b.Value == nil { 14 | return []string{"default-module"}, nil 15 | } 16 | 17 | return b.Value, nil 18 | } 19 | 20 | func (b BuildServiceMock) FindPomFile(_ string) (string, error) { 21 | if b.Err != nil { 22 | return "", b.Err 23 | } 24 | 25 | return "pom.xml", nil 26 | } 27 | 28 | func (b BuildServiceMock) RenamePomToXml(pomFile, destDir string) (string, error) { 29 | if b.Err != nil { 30 | return "", b.Err 31 | } 32 | 33 | return pomFile, nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | "runtime" 6 | ) 7 | 8 | type CmdFactoryMock struct { 9 | Err error 10 | Name string 11 | Arg string 12 | } 13 | 14 | func (f CmdFactoryMock) MakePomCmd(_ string) (*exec.Cmd, error) { 15 | if len(f.Arg) == 0 { 16 | f.Arg = `"MakePomCmd"` 17 | } 18 | 19 | if runtime.GOOS == "windows" && f.Name == "echo" { 20 | return exec.Command("cmd", "/C", f.Name, f.Arg), f.Err 21 | } 22 | 23 | return exec.Command(f.Name, f.Arg), f.Err 24 | } 25 | -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/testdata/invalidBuild.sbt: -------------------------------------------------------------------------------- 1 | name := "invalid-project" 2 | version := "1.0.0" 3 | 4 | libraryDependencies ++= Seq( 5 | "org.scala-lang" % "scala-library" % "2.13.8", 6 | // Missing closing parenthesis 7 | "com.typesafe.akka" %% "akka-http" % "10.2.9" 8 | "com.typesafe.akka" %% "akka-stream" % "10.2.9" 9 | ) 10 | 11 | // Invalid syntax 12 | scalaVersion = "2.13.8" -------------------------------------------------------------------------------- /internal/resolution/pm/sbt/testdata/notABuild.sbt: -------------------------------------------------------------------------------- 1 | org.scala-lang:scala-library:2.13.8 2 | com.typesafe.akka:akka-http:10.2.9 3 | # This is not a valid SBT build file -------------------------------------------------------------------------------- /internal/resolution/pm/testdata/pm_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type PmMock struct { 4 | N string 5 | Ms []string 6 | } 7 | 8 | func (pm PmMock) Name() string { 9 | return pm.N 10 | } 11 | 12 | func (pm PmMock) Manifests() []string { 13 | return pm.Ms 14 | } 15 | -------------------------------------------------------------------------------- /internal/resolution/pm/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/debricked/cli/internal/resolution/job" 10 | "github.com/debricked/cli/internal/resolution/pm/writer" 11 | ) 12 | 13 | func MakePathFromManifestFile(siblingFile string, fileName string) string { 14 | dir := filepath.Dir(siblingFile) 15 | if strings.EqualFold(string(os.PathSeparator), dir) { 16 | return fmt.Sprintf("%s%s", string(os.PathSeparator), fileName) 17 | } 18 | 19 | return fmt.Sprintf("%s%s%s", dir, string(os.PathSeparator), fileName) 20 | } 21 | 22 | func CloseFile(j job.IJob, fileWriter writer.IFileWriter, file *os.File) { 23 | err := fileWriter.Close(file) 24 | if err != nil { 25 | j.Errors().Critical(NewPMJobError(err.Error())) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/writer/file_writer.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type IFileWriter interface { 8 | Write(file *os.File, p []byte) error 9 | Create(name string) (*os.File, error) 10 | Close(file *os.File) error 11 | } 12 | 13 | type FileWriter struct{} 14 | 15 | func (fw FileWriter) Create(name string) (*os.File, error) { 16 | return os.Create(name) 17 | } 18 | 19 | func (fw FileWriter) Write(file *os.File, p []byte) error { 20 | _, err := file.Write(p) 21 | 22 | return err 23 | } 24 | 25 | func (fw FileWriter) Close(file *os.File) error { 26 | return file.Close() 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/writer/file_writer_test.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var fw = FileWriter{} 11 | 12 | const fileName = "debricked-test.json" 13 | 14 | func TestCreate(t *testing.T) { 15 | testFile, err := fw.Create(fileName) 16 | assert.NoError(t, err) 17 | assert.NotNil(t, testFile) 18 | defer deleteFile(t, testFile) 19 | } 20 | 21 | func TestWrite(t *testing.T) { 22 | content := []byte("{}") 23 | testFile, _ := fw.Create(fileName) 24 | defer deleteFile(t, testFile) 25 | 26 | err := fw.Write(testFile, content) 27 | 28 | assert.NoError(t, err) 29 | fileContents, err := os.ReadFile(fileName) 30 | assert.NoError(t, err) 31 | assert.Equal(t, fileContents, content) 32 | } 33 | 34 | func TestClose(t *testing.T) { 35 | testFile, _ := fw.Create(fileName) 36 | defer deleteFile(t, testFile) 37 | 38 | err := fw.Close(testFile) 39 | 40 | assert.NoError(t, err) 41 | } 42 | 43 | func deleteFile(t *testing.T, file *os.File) { 44 | _ = file.Close() 45 | err := os.Remove(file.Name()) 46 | assert.NoError(t, err) 47 | } 48 | -------------------------------------------------------------------------------- /internal/resolution/pm/writer/testdata/file_writer_mock.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type FileWriterMock struct { 8 | file *os.File 9 | Contents []byte 10 | CreateErr error 11 | WriteErr error 12 | CloseErr error 13 | } 14 | 15 | func (fw *FileWriterMock) Create(_ string) (*os.File, error) { 16 | return fw.file, fw.CreateErr 17 | } 18 | 19 | func (fw *FileWriterMock) Write(_ *os.File, bytes []byte) error { 20 | fw.Contents = append(fw.Contents, bytes...) 21 | 22 | return fw.WriteErr 23 | } 24 | 25 | func (fw *FileWriterMock) Close(_ *os.File) error { 26 | return fw.CloseErr 27 | } 28 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/README.md: -------------------------------------------------------------------------------- 1 | # Yarn resolution logic 2 | 3 | The way resolution of yarn lock files works is as follows: 4 | 5 | 1. Run `install --non-interactive --ignore-scripts --ignore-engines --ignore-platform --no-bin-link --production=false` in order to install all dependencies 6 | 7 | Generated `yarn.lock` file is then uploaded together with `package.json` for scanning. 8 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/cmd_factory.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | import ( 4 | "os/exec" 5 | "path/filepath" 6 | ) 7 | 8 | type ICmdFactory interface { 9 | MakeInstallCmd(command string, file string) (*exec.Cmd, error) 10 | } 11 | 12 | type IExecPath interface { 13 | LookPath(file string) (string, error) 14 | } 15 | 16 | type ExecPath struct { 17 | } 18 | 19 | func (ExecPath) LookPath(file string) (string, error) { 20 | return exec.LookPath(file) 21 | } 22 | 23 | type CmdFactory struct { 24 | execPath IExecPath 25 | } 26 | 27 | func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 28 | path, err := cmdf.execPath.LookPath(command) 29 | 30 | fileDir := filepath.Dir(file) 31 | 32 | return &exec.Cmd{ 33 | Path: path, 34 | Args: []string{command, "install", 35 | "--non-interactive", // We can't answer any prompts... 36 | "--ignore-scripts", // Avoid risky scripts 37 | "--ignore-engines", // We won't run the code, so we don't care about the engine versions 38 | "--ignore-platform", // We won't run the code, so we don't care about the platform, undocumented option 39 | "--no-bin-links", // We don't need symlinks to binaries as we won't run any code 40 | "--production=false", // Always include dev dependencies 41 | }, 42 | Dir: fileDir, 43 | }, err 44 | } 45 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/cmd_factory_test.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeInstallCmd(t *testing.T) { 10 | yarnCommand := "yarn" 11 | cmd, err := CmdFactory{ 12 | execPath: ExecPath{}, 13 | }.MakeInstallCmd(yarnCommand, "file") 14 | assert.NoError(t, err) 15 | assert.NotNil(t, cmd) 16 | args := cmd.Args 17 | assert.Contains(t, args, "yarn") 18 | assert.Contains(t, args, "install") 19 | } 20 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/pm.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | const Name = "yarn" 4 | 5 | type Pm struct { 6 | name string 7 | } 8 | 9 | func NewPm() Pm { 10 | return Pm{ 11 | name: Name, 12 | } 13 | } 14 | 15 | func (pm Pm) Name() string { 16 | return pm.name 17 | } 18 | 19 | func (Pm) Manifests() []string { 20 | return []string{ 21 | `package\.json$`, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/pm_test.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewPm(t *testing.T) { 11 | pm := NewPm() 12 | assert.Equal(t, Name, pm.name) 13 | } 14 | 15 | func TestName(t *testing.T) { 16 | pm := NewPm() 17 | assert.Equal(t, Name, pm.Name()) 18 | } 19 | 20 | func TestManifests(t *testing.T) { 21 | pm := Pm{} 22 | manifests := pm.Manifests() 23 | assert.Len(t, manifests, 1) 24 | manifest := manifests[0] 25 | assert.Equal(t, `package\.json$`, manifest) 26 | _, err := regexp.Compile(manifest) 27 | assert.NoError(t, err) 28 | 29 | cases := map[string]bool{ 30 | "package.json": true, 31 | "package-lock.json": false, 32 | "yarn.lock": false, 33 | } 34 | for file, isMatch := range cases { 35 | t.Run(file, func(t *testing.T) { 36 | matched, _ := regexp.MatchString(manifest, file) 37 | assert.Equal(t, isMatch, matched) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/strategy.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type Strategy struct { 8 | files []string 9 | } 10 | 11 | func (s Strategy) Invoke() ([]job.IJob, error) { 12 | var jobs []job.IJob 13 | for _, file := range s.files { 14 | jobs = append(jobs, NewJob( 15 | file, 16 | true, 17 | CmdFactory{ 18 | execPath: ExecPath{}, 19 | }, 20 | ), 21 | ) 22 | } 23 | 24 | return jobs, nil 25 | } 26 | 27 | func NewStrategy(files []string) Strategy { 28 | return Strategy{files} 29 | } 30 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/strategy_test.go: -------------------------------------------------------------------------------- 1 | package yarn 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewStrategy(t *testing.T) { 10 | s := NewStrategy(nil) 11 | assert.NotNil(t, s) 12 | assert.Len(t, s.files, 0) 13 | 14 | s = NewStrategy([]string{}) 15 | assert.NotNil(t, s) 16 | assert.Len(t, s.files, 0) 17 | 18 | s = NewStrategy([]string{"file"}) 19 | assert.NotNil(t, s) 20 | assert.Len(t, s.files, 1) 21 | 22 | s = NewStrategy([]string{"file-1", "file-2"}) 23 | assert.NotNil(t, s) 24 | assert.Len(t, s.files, 2) 25 | } 26 | 27 | func TestInvokeNoFiles(t *testing.T) { 28 | s := NewStrategy([]string{}) 29 | jobs, _ := s.Invoke() 30 | assert.Empty(t, jobs) 31 | } 32 | 33 | func TestInvokeOneFile(t *testing.T) { 34 | s := NewStrategy([]string{"file"}) 35 | jobs, _ := s.Invoke() 36 | assert.Len(t, jobs, 1) 37 | } 38 | 39 | func TestInvokeManyFiles(t *testing.T) { 40 | s := NewStrategy([]string{"file-1", "file-2"}) 41 | jobs, _ := s.Invoke() 42 | assert.Len(t, jobs, 2) 43 | } 44 | -------------------------------------------------------------------------------- /internal/resolution/pm/yarn/testdata/cmd_factory_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | type CmdFactoryMock struct { 8 | InstallCmdName string 9 | MakeInstallErr error 10 | } 11 | 12 | func NewEchoCmdFactory() CmdFactoryMock { 13 | return CmdFactoryMock{ 14 | InstallCmdName: "echo", 15 | } 16 | } 17 | 18 | func (f CmdFactoryMock) MakeInstallCmd(command string, file string) (*exec.Cmd, error) { 19 | return exec.Command(f.InstallCmdName), f.MakeInstallErr 20 | } 21 | -------------------------------------------------------------------------------- /internal/resolution/resolution.go: -------------------------------------------------------------------------------- 1 | package resolution 2 | 3 | import "github.com/debricked/cli/internal/resolution/job" 4 | 5 | type IResolution interface { 6 | Jobs() []job.IJob 7 | HasErr() bool 8 | GetJobErrorCount() int 9 | } 10 | 11 | type Resolution struct { 12 | jobs []job.IJob 13 | } 14 | 15 | func NewResolution(jobs []job.IJob) Resolution { 16 | return Resolution{jobs} 17 | } 18 | 19 | func (r Resolution) Jobs() []job.IJob { 20 | return r.jobs 21 | } 22 | 23 | func (r Resolution) HasErr() bool { 24 | for _, j := range r.Jobs() { 25 | if j.Errors().HasError() { 26 | return true 27 | } 28 | } 29 | 30 | return false 31 | } 32 | 33 | func (r Resolution) GetJobErrorCount() int { 34 | count := 0 35 | for _, j := range r.Jobs() { 36 | if j.Errors().HasError() { 37 | count++ 38 | } 39 | } 40 | 41 | return count 42 | } 43 | -------------------------------------------------------------------------------- /internal/resolution/resolution_test.go: -------------------------------------------------------------------------------- 1 | package resolution 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/debricked/cli/internal/resolution/job" 7 | "github.com/debricked/cli/internal/resolution/job/testdata" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestNewResolution(t *testing.T) { 12 | res := NewResolution(nil) 13 | assert.NotNil(t, res) 14 | 15 | res = NewResolution([]job.IJob{}) 16 | assert.NotNil(t, res) 17 | 18 | res = NewResolution([]job.IJob{testdata.NewJobMock("")}) 19 | assert.NotNil(t, res) 20 | 21 | res = NewResolution([]job.IJob{testdata.NewJobMock(""), testdata.NewJobMock("")}) 22 | assert.NotNil(t, res) 23 | } 24 | 25 | func TestJobs(t *testing.T) { 26 | res := NewResolution(nil) 27 | assert.Empty(t, res.Jobs()) 28 | 29 | res.jobs = []job.IJob{} 30 | assert.Len(t, res.Jobs(), 0) 31 | 32 | res.jobs = []job.IJob{testdata.NewJobMock("")} 33 | assert.Len(t, res.Jobs(), 1) 34 | 35 | res.jobs = []job.IJob{testdata.NewJobMock(""), testdata.NewJobMock("")} 36 | assert.Len(t, res.Jobs(), 2) 37 | } 38 | 39 | func TestHasError(t *testing.T) { 40 | res := NewResolution(nil) 41 | assert.False(t, res.HasErr()) 42 | 43 | res.jobs = []job.IJob{testdata.NewJobMock("")} 44 | assert.False(t, res.HasErr()) 45 | 46 | jobMock := testdata.NewJobMock("") 47 | jobMock.SetErr(job.NewBaseJobError("error")) 48 | res.jobs = append(res.jobs, jobMock) 49 | assert.True(t, res.HasErr()) 50 | } 51 | -------------------------------------------------------------------------------- /internal/resolution/strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/job" 5 | ) 6 | 7 | type IStrategy interface { 8 | Invoke() ([]job.IJob, error) 9 | } 10 | -------------------------------------------------------------------------------- /internal/resolution/strategy/testdata/strategy_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/debricked/cli/internal/resolution/job" 7 | "github.com/debricked/cli/internal/resolution/job/testdata" 8 | ) 9 | 10 | type StrategyMock struct { 11 | files []string 12 | } 13 | 14 | func NewStrategyMock(files []string) StrategyMock { 15 | return StrategyMock{files} 16 | } 17 | 18 | func (s StrategyMock) Invoke() ([]job.IJob, error) { 19 | var jobs []job.IJob 20 | for _, file := range s.files { 21 | jobs = append(jobs, testdata.NewJobMock(file)) 22 | } 23 | 24 | return jobs, nil 25 | } 26 | 27 | type StrategyErrorMock struct { 28 | files []string 29 | } 30 | 31 | func NewStrategyErrorMock(files []string) StrategyErrorMock { 32 | return StrategyErrorMock{files} 33 | } 34 | 35 | func (s StrategyErrorMock) Invoke() ([]job.IJob, error) { 36 | 37 | return nil, errors.New("mock-error") 38 | } 39 | -------------------------------------------------------------------------------- /internal/resolution/strategy/testdata/strategy_mock_factory.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/resolution/file" 5 | "github.com/debricked/cli/internal/resolution/strategy" 6 | ) 7 | 8 | type FactoryMock struct{} 9 | 10 | func NewStrategyFactoryMock() FactoryMock { 11 | return FactoryMock{} 12 | } 13 | 14 | func (sf FactoryMock) Make(pmFileBatch file.IBatch, paths []string) (strategy.IStrategy, error) { 15 | 16 | return NewStrategyMock(pmFileBatch.Files()), nil 17 | } 18 | 19 | type FactoryErrorMock struct{} 20 | 21 | func NewStrategyFactoryErrorMock() FactoryErrorMock { 22 | return FactoryErrorMock{} 23 | } 24 | 25 | func (sf FactoryErrorMock) Make(pmFileBatch file.IBatch, paths []string) (strategy.IStrategy, error) { 26 | 27 | return NewStrategyErrorMock(pmFileBatch.Files()), nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/resolution/testdata/resolver_mock.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/debricked/cli/internal/resolution" 8 | "github.com/debricked/cli/internal/resolution/job" 9 | ) 10 | 11 | type ResolverMock struct { 12 | Err error 13 | files []string 14 | } 15 | 16 | func (r *ResolverMock) SetNpmPreferred(_ bool) { 17 | } 18 | 19 | func (r *ResolverMock) Resolve(_ []string, _ resolution.IOptions) (resolution.IResolution, error) { 20 | for _, f := range r.files { 21 | createdFile, err := os.Create(f) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | err = createdFile.Close() 27 | if err != nil { 28 | return nil, err 29 | } 30 | } 31 | 32 | return resolution.NewResolution([]job.IJob{}), r.Err 33 | } 34 | 35 | func (r *ResolverMock) SetFiles(files []string) { 36 | r.files = files 37 | } 38 | 39 | func (r *ResolverMock) CleanUp() error { 40 | for _, f := range r.files { 41 | abs, err := filepath.Abs(f) 42 | if err != nil { 43 | return err 44 | } 45 | err = os.Remove(abs) 46 | if err != nil { 47 | return err 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /internal/runtime/os/os.go: -------------------------------------------------------------------------------- 1 | package os 2 | 3 | const ( 4 | Windows = "windows" 5 | ) 6 | -------------------------------------------------------------------------------- /internal/scan/testdata/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "templates", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:debricked/templates.git", 6 | "author": "viktigpetterr ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "lodash": "^4.17.21" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/tui/dependency.go: -------------------------------------------------------------------------------- 1 | package tui 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/debricked/cli/internal/automation" 7 | ) 8 | 9 | type dependency struct { 10 | name string 11 | url string 12 | vulnerabilities map[string]vulnerability 13 | licenses map[string]bool 14 | } 15 | 16 | func makeDependenciesFromTriggers(triggers []automation.TriggerEvent) map[string]dependency { 17 | dependencies := map[string]dependency{} 18 | 19 | for _, trigger := range triggers { 20 | dep, ok := dependencies[trigger.Dependency] 21 | if !ok { 22 | dep = dependency{ 23 | name: trigger.Dependency, 24 | url: trigger.DependencyLink, 25 | vulnerabilities: map[string]vulnerability{}, 26 | licenses: map[string]bool{}, 27 | } 28 | dependencies[dep.name] = dep 29 | } 30 | 31 | for _, license := range trigger.Licenses { 32 | if len(license) > 0 { 33 | dep.licenses[license] = true 34 | } 35 | } 36 | 37 | if _, ok = dep.vulnerabilities[trigger.Cve]; !ok && len(trigger.Cve) > 0 { 38 | dep.vulnerabilities[trigger.Cve] = vulnerability{ 39 | name: trigger.Cve, 40 | url: trigger.CveLink, 41 | cvss2: fmt.Sprintf("%g", trigger.Cvss2), 42 | cvss3: fmt.Sprintf("%g", trigger.Cvss3), 43 | } 44 | } 45 | } 46 | 47 | return dependencies 48 | } 49 | -------------------------------------------------------------------------------- /internal/tui/progress_bar.go: -------------------------------------------------------------------------------- 1 | package tui 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/fatih/color" 7 | "github.com/schollz/progressbar/v3" 8 | ) 9 | 10 | func NewProgressBar() *progressbar.ProgressBar { 11 | return progressbar.NewOptions(100, 12 | progressbar.OptionEnableColorCodes(true), 13 | progressbar.OptionSetPredictTime(true), 14 | progressbar.OptionSetWidth(30), 15 | progressbar.OptionSetDescription("[blue]Scanning...[reset]"), 16 | progressbar.OptionOnCompletion(func() { 17 | color.NoColor = false 18 | checkmark := color.GreenString("✔") 19 | fmt.Println(checkmark) 20 | }), 21 | progressbar.OptionSetTheme(progressbar.Theme{ 22 | Saucer: "[blue]█[reset]", 23 | SaucerPadding: " ", 24 | BarStart: "|", 25 | BarEnd: "|", 26 | }), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /internal/tui/progress_bar_test.go: -------------------------------------------------------------------------------- 1 | package tui 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewProgressBar(t *testing.T) { 10 | bar := NewProgressBar() 11 | 12 | assert.NotNil(t, bar) 13 | assert.False(t, bar.IsFinished(), "failed to assert that the bar was not finished") 14 | 15 | err := bar.Set(100) 16 | assert.NoError(t, err) 17 | assert.True(t, bar.IsFinished(), "failed to assert that the bar was finished") 18 | } 19 | -------------------------------------------------------------------------------- /internal/tui/vulnerability.go: -------------------------------------------------------------------------------- 1 | package tui 2 | 3 | type vulnerability struct { 4 | name string 5 | url string 6 | cvss2 string 7 | cvss3 string 8 | } 9 | -------------------------------------------------------------------------------- /internal/upload/result.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "github.com/debricked/cli/internal/automation" 5 | ) 6 | 7 | type UploadResult struct { 8 | VulnerabilitiesFound int `json:"vulnerabilitiesFound"` 9 | UnaffectedVulnerabilitiesFound int `json:"unaffectedVulnerabilitiesFound"` 10 | AutomationsAction string `json:"automationsAction"` 11 | AutomationRules []automation.Rule `json:"automationRules"` 12 | DetailsUrl string `json:"detailsUrl"` 13 | LongQueue bool 14 | } 15 | 16 | func newUploadResult(status *uploadStatus) *UploadResult { 17 | return &UploadResult{ 18 | status.VulnerabilitiesFound, 19 | status.UnaffectedVulnerabilitiesFound, 20 | status.AutomationsAction, 21 | status.AutomationRules, 22 | status.DetailsUrl, 23 | false, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /internal/upload/result_test.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewUploadResult(t *testing.T) { 10 | status := &uploadStatus{ 11 | Progress: 100, 12 | VulnerabilitiesFound: 0, 13 | UnaffectedVulnerabilitiesFound: 0, 14 | AutomationsAction: "", 15 | AutomationRules: nil, 16 | DetailsUrl: "", 17 | } 18 | result := newUploadResult(status) 19 | 20 | assert.NotNil(t, result) 21 | } 22 | -------------------------------------------------------------------------------- /internal/upload/status.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | 8 | "github.com/debricked/cli/internal/automation" 9 | ) 10 | 11 | type uploadStatus struct { 12 | Progress int `json:"progress"` 13 | VulnerabilitiesFound int `json:"vulnerabilitiesFound"` 14 | UnaffectedVulnerabilitiesFound int `json:"unaffectedVulnerabilitiesFound"` 15 | AutomationsAction string `json:"automationsAction"` 16 | AutomationRules []automation.Rule `json:"automationRules"` 17 | DetailsUrl string `json:"detailsUrl"` 18 | } 19 | 20 | func newUploadStatus(response *http.Response) (*uploadStatus, error) { 21 | status := uploadStatus{} 22 | data, _ := io.ReadAll(response.Body) 23 | defer response.Body.Close() 24 | err := json.Unmarshal(data, &status) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return &status, err 30 | } 31 | -------------------------------------------------------------------------------- /internal/upload/status_test.go: -------------------------------------------------------------------------------- 1 | package upload 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewScanStatusBadResponse(t *testing.T) { 11 | res := &http.Response{ 12 | Status: "", 13 | StatusCode: 0, 14 | Proto: "", 15 | ProtoMajor: 0, 16 | ProtoMinor: 0, 17 | Header: nil, 18 | Body: http.NoBody, 19 | ContentLength: 0, 20 | TransferEncoding: nil, 21 | Close: false, 22 | Uncompressed: false, 23 | Trailer: nil, 24 | Request: nil, 25 | TLS: nil, 26 | } 27 | status, err := newUploadStatus(res) 28 | 29 | assert.Error(t, err) 30 | assert.Nil(t, status) 31 | } 32 | -------------------------------------------------------------------------------- /internal/upload/testdata/debricked-config-error.yaml: -------------------------------------------------------------------------------- 1 | overrides: 2 | - pURL: "pkg:npm/lodash" 3 | version: "1.0.0" # optional: if left out, we will decide version 4 | fileRegex: ".*/lodash/.*" # PCRE2 5 | - pURL: "pkg:maven/org.openjfx/javafx-base" % ERROR 6 | ¤¤ 7 | -------------------------------------------------------------------------------- /internal/upload/testdata/debricked-config.yaml: -------------------------------------------------------------------------------- 1 | overrides: 2 | - pURL: "pkg:npm/lodash" 3 | version: "1.0.0" # optional: if left out, we will decide version 4 | fileRegexes: 5 | - ".*/lodash/.*" # PCRE2 6 | - pURL: "pkg:maven/org.openjfx/javafx-base" 7 | fileRegexes: 8 | - "subpath/org.openjfx/.*" 9 | -------------------------------------------------------------------------------- /internal/upload/testdata/misc/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/internal/upload/testdata/misc/requirements.txt -------------------------------------------------------------------------------- /internal/upload/testdata/yarn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "templates", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:debricked/templates.git", 6 | "author": "viktigpetterr ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "express": "^4.17.3" 10 | } 11 | } -------------------------------------------------------------------------------- /internal/wire/cli_container_test.go: -------------------------------------------------------------------------------- 1 | package wire 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestWire(t *testing.T) { 10 | cliContainer = &CliContainer{} 11 | defer resetContainer() 12 | 13 | err := cliContainer.wire() 14 | assert.NoError(t, err) 15 | assertCliContainer(t, cliContainer) 16 | } 17 | 18 | func TestGetCliContainer(t *testing.T) { 19 | assert.Nil(t, cliContainer) 20 | testGetCliContainer(t) 21 | } 22 | 23 | func testGetCliContainer(t *testing.T) { 24 | container := GetCliContainer() 25 | assert.NotNil(t, container) 26 | assert.NotNil(t, cliContainer) 27 | assertCliContainer(t, cliContainer) 28 | } 29 | 30 | func resetContainer() { 31 | cliContainer = nil 32 | } 33 | 34 | func assertCliContainer(t *testing.T, cc *CliContainer) { 35 | assert.NotNil(t, cc.DebClient()) 36 | assert.NotNil(t, cc.Finder()) 37 | assert.NotNil(t, cc.Scanner()) 38 | assert.NotNil(t, cc.Resolver()) 39 | assert.NotNil(t, cc.CallgraphGenerator()) 40 | assert.NotNil(t, cc.LicenseReporter()) 41 | assert.NotNil(t, cc.VulnerabilityReporter()) 42 | assert.NotNil(t, cc.Fingerprinter()) 43 | assert.NotNil(t, cc.Authenticator()) 44 | assert.NotNil(t, cc.SBOMReporter()) 45 | } 46 | -------------------------------------------------------------------------------- /internal/wire/container.go: -------------------------------------------------------------------------------- 1 | package wire 2 | 3 | type IContainer interface { 4 | wire() error 5 | } 6 | -------------------------------------------------------------------------------- /scripts/fetch_supported_formats.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "${0%/*}/../" 4 | mkdir -p internal/file/embedded 5 | curl -fsSLo internal/file/embedded/supported_formats.json https://debricked.com/api/1.0/open/files/supported-formats 6 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | # test if git is installed 6 | if ! command -v git >/dev/null 2>&1 7 | then 8 | echo -e "Failed to find git, thus also the version. Version will be set to v0.0.0" 9 | fi 10 | set +e 11 | version=${DEBRICKED_VERSION:-$(git symbolic-ref -q --short HEAD || git describe --tags --exact-match)} 12 | set -e 13 | ldFlags="-X main.version=${version}" 14 | go install -ldflags "${ldFlags}" ./cmd/debricked 15 | go generate -v -x ./cmd/debricked 16 | go build -ldflags "${ldFlags}" ./cmd/debricked 17 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GREEN='\033[0;32m' 3 | RED='\033[0;31m' 4 | YELLOW='\033[0;33m' 5 | SET='\033[0m' 6 | if ! command -v golangci-lint &> /dev/null 7 | then 8 | echo -e "${YELLOW}golangci-lint${SET} could not be found. Make sure it is installed" 9 | echo -e "${RED}FAILED${SET}" 10 | exit 11 | fi 12 | if ! golangci-lint run ./...; 13 | then 14 | echo -e "${RED}FAILED${SET}" 15 | exit 16 | else 17 | echo -e "${GREEN}OK${SET}" 18 | fi -------------------------------------------------------------------------------- /scripts/test_cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GREEN='\033[0;32m' 3 | RED='\033[0;31m' 4 | SET='\033[0m' 5 | set -e 6 | 7 | go test -cover -coverprofile=coverage.out ./internal/... 8 | 9 | echo -e "\nChecking test coverage threshold..." 10 | regex='[0-9]+\.*[0-9]*' 11 | if ! [[ $TEST_COVERAGE_THRESHOLD =~ $regex ]]; then 12 | echo "Failed to find test coverage threshold. Defaults to 95%" 13 | TEST_COVERAGE_THRESHOLD=95 14 | fi 15 | echo "Test coverage threshold : $TEST_COVERAGE_THRESHOLD %" 16 | if [ ! -f "./coverage.out" ]; then 17 | echo "Failed to find coverage.out. Make sure coverage.out is created" 18 | echo -e "${RED}FAILED${SET}" 19 | exit 1 20 | fi 21 | 22 | # Find test coverage 23 | totalTestCoverage=$(go tool cover -func=coverage.out | grep total | grep -Eo "$regex") 24 | # Store coverage report 25 | go tool cover -html=coverage.out -o=coverage.html 26 | 27 | echo "Current test coverage : $totalTestCoverage %" 28 | if (( $(echo "$totalTestCoverage $TEST_COVERAGE_THRESHOLD" | awk '{print ($1 > $2)}') )); then 29 | echo -e "${GREEN}OK${SET}" 30 | else 31 | echo "Current test coverage in below threshold. Please extend your unit tests" 32 | echo -e "${RED}FAILED${SET}" 33 | exit 1 34 | fi 35 | -------------------------------------------------------------------------------- /scripts/test_e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash/env 2 | 3 | type="$1" 4 | 5 | case $type in 6 | "resolver") 7 | go test -timeout 120s ./test/resolve/resolver_test.go 8 | ;; 9 | "maven") 10 | go test -v ./test/callgraph/maven_test.go 11 | ;; 12 | *) 13 | go test -timeout 120s ./test/... 14 | ;; 15 | esac 16 | -------------------------------------------------------------------------------- /scripts/test_e2e_callgraph_java_version.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$1" ]; then 2 | DEBRICKED_JAVA_VERSION=11 3 | else 4 | DEBRICKED_JAVA_VERSION=$1 5 | fi 6 | 7 | sed -i "s/[0-9]\+<\/java.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml 8 | go test -v ./test/callgraph/maven_test.go 9 | -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-build/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.test 7 | mvnproj-build 8 | 1.0 9 | 10 | 11 | 11 12 | ${java.version} 13 | ${java.version} 14 | 15 | 16 | 17 | 18 | org.junit.jupiter 19 | junit-jupiter-params 20 | 5.9.2 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-build/src/main/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | toString("Hello, World!"); 4 | } 5 | 6 | private static void toString(String s) { 7 | System.out.println(s); 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-no-build/.debrickedTmpFolder/ignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/test/callgraph/testdata/mvnproj-no-build/.debrickedTmpFolder/ignore -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-no-build/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.test 8 | mvnproj-build 9 | 1.0 10 | 11 | 12 | 11 13 | 11 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-no-build/src/main/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | toString("Hello, World!"); 4 | } 5 | 6 | private static void toString(String s) { 7 | System.out.println(s); 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /test/callgraph/testdata/mvnproj-no-build/target/classes/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debricked/cli/b6f4cfaa9b12798260def087521d43731401db46/test/callgraph/testdata/mvnproj-no-build/target/classes/HelloWorld.class -------------------------------------------------------------------------------- /test/checkFilesResolved.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GREEN='\033[0;32m' 3 | RED='\033[0;31m' 4 | 5 | # This script checks if all files in the current directory exist 6 | function check_files_exist() { 7 | isOk=true 8 | for file in "$@"; do 9 | if test -f "$file"; then 10 | echo -e "${GREEN}File $file OK${GREEN}" 11 | else 12 | echo -e "${RED}File $file does not exist!${RED}" 13 | isOk=false 14 | fi 15 | done 16 | 17 | if [ "$isOk" = false ]; then 18 | exit 1 19 | fi 20 | 21 | } 22 | 23 | check_files_exist "test/resolve/testdata/npm/yarn.lock" \ 24 | "test/resolve/testdata/pip/requirements.txt.pip.debricked.lock" \ 25 | "test/resolve/testdata/nuget/packagesconfig/packages.config.nuget.debricked.lock" \ 26 | "test/resolve/testdata/nuget/csproj/packages.lock.json" \ 27 | "test/resolve/testdata/gradle/gradle.debricked.lock" \ 28 | "test/resolve/testdata/maven/maven.debricked.lock" \ 29 | "test/resolve/testdata/gomod/gomod.debricked.lock" -------------------------------------------------------------------------------- /test/resolve/testdata/bower/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli-test", 3 | "main": "main.js", 4 | "dependencies": { 5 | "ev-emitter": "^1.0.0", 6 | "get-size": "^2.0.2", 7 | "fizzy-ui-utils": "^2.0.0" 8 | }, 9 | "devDependencies": { 10 | "jquery-bridget": "2.x", 11 | "jquery": ">=1.4.3 <4", 12 | "qunit": "^2.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/resolve/testdata/composer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debricked/test", 3 | "require": { 4 | "php": ">=8.2.0", 5 | "ext-curl": "*", 6 | "ext-dom": "*", 7 | "doctrine/annotations": "^2.0", 8 | "justinrainbow/json-schema": "^5.2", 9 | "psr/log": "^1.0.1" 10 | }, 11 | "require-dev": { 12 | "phpstan/phpstan": "^1.4.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/resolve/testdata/gomod/go.mod: -------------------------------------------------------------------------------- 1 | module resolvetest 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 7 | github.com/spf13/cobra v1.3.0 // indirect 8 | github.com/spf13/pflag v1.0.5 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /test/resolve/testdata/gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | implementation 'org.apache.logging.log4j:log4j-core:2.20.0' 11 | } 12 | 13 | java { 14 | sourceCompatibility = JavaVersion.VERSION_1_8 15 | targetCompatibility = JavaVersion.VERSION_1_8 16 | } 17 | -------------------------------------------------------------------------------- /test/resolve/testdata/maven/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.example 8 | my-app 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | org.apache.logging.log4j 19 | log4j-core 20 | 2.20.0 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/resolve/testdata/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "chart.js": "2.9.3" 4 | } 5 | } -------------------------------------------------------------------------------- /test/resolve/testdata/nuget/csproj/basic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/resolve/testdata/nuget/packagesconfig/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/resolve/testdata/pip/requirements.txt: -------------------------------------------------------------------------------- 1 | Cython==3.0.5 --------------------------------------------------------------------------------