├── .containerignore ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── actions │ ├── cache-go-dependencies │ │ └── action.yaml │ ├── handle-tagged-build │ │ └── action.yaml │ └── job-preamble │ │ └── action.yaml ├── dependabot.yml ├── renovate.json5 └── workflows │ ├── add-new-pr-to-oss-triaging.yml │ ├── ci.yaml │ └── sanity-check-vuln-updates.yaml ├── .gitignore ├── .golangci.yml ├── .konflux ├── README.md └── scanner-data │ └── .gitignore ├── .openshift-ci ├── Dockerfile.build_root ├── build │ ├── Dockerfile.build-bundle │ ├── Dockerfile.build-db-bundle │ ├── Dockerfile.generate-db-dump │ ├── Dockerfile.generate-genesis-dump │ ├── build-bundle.sh │ ├── build-db-bundle.sh │ ├── generate-db-dump.sh │ └── generate-genesis-dump.sh ├── ci_tests.py ├── clusters.py ├── common.py ├── dispatch.sh ├── nightlies.sh ├── post_tests.py ├── pre_tests.py └── runners.py ├── .ossls.yml ├── .syft.yaml ├── .tekton ├── scanner-build.yaml ├── scanner-component-pipeline.yaml ├── scanner-db-build.yaml ├── scanner-db-slim-build.yaml └── scanner-slim-build.yaml ├── BUILD_IMAGE_VERSION ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Makefile ├── README.md ├── api ├── api.go ├── grpc │ ├── grpc.go │ ├── logger.go │ ├── logging_interceptor.go │ ├── slim_mode_interceptor.go │ └── verify_peer_certs_interceptor.go └── v1 │ ├── common │ ├── convert.go │ ├── dep_map.go │ ├── dep_map_test.go │ ├── feature_key_set.go │ └── feature_key_set_test.go │ ├── convert │ ├── convert.go │ ├── convert_test.go │ ├── istio.go │ ├── k8s.go │ ├── k8s_convert_test.go │ ├── severity.go │ ├── version.go │ └── version_test.go │ ├── features │ └── convert.go │ ├── imagescan │ ├── convert.go │ ├── service.go │ └── types.go │ ├── models.go │ ├── models_language.go │ ├── models_language_test.go │ ├── models_rhelv2.go │ ├── models_rhelv2_test.go │ ├── models_test.go │ ├── nodeinventory │ └── service.go │ ├── nodescan │ ├── service.go │ └── service_test.go │ ├── note_string.go │ ├── orchestratorscan │ ├── openshift_test.go │ ├── openshift_version.go │ └── service.go │ ├── ping │ └── service.go │ ├── testdata │ └── nvd.json │ ├── v1_easyjson.go │ └── vulndefs │ └── service.go ├── benchmarks ├── analyzeNode │ └── analyze_node_test.go ├── detectcontent │ └── detect_content_test.go └── util.go ├── bin └── .gitignore ├── chart ├── Chart.yaml ├── templates │ ├── configmap.yaml │ ├── db-deployment.yaml │ ├── deployment.yaml │ ├── mock-scanner-db-tls.yaml │ ├── mock-scanner-tls.yaml │ ├── secret.yaml │ └── service.yaml └── values.yaml ├── cmd ├── clair │ ├── config.go │ ├── config_test.go │ ├── main.go │ └── testdata │ │ └── config.yaml └── updater │ ├── common │ └── dump_utils.go │ ├── diffdumps │ ├── cmd.go │ ├── cmd_test.go │ └── rhelv2_diff.go │ ├── generatedump │ └── cmd.go │ ├── loaddump │ └── cmd.go │ ├── main.go │ └── printstats │ └── cmd.go ├── cpe ├── attributes │ ├── common │ │ ├── common.go │ │ └── common_test.go │ ├── dotnetcoreruntime │ │ └── dotnetcoreruntime.go │ ├── java │ │ └── java.go │ ├── node │ │ └── node.go │ ├── python │ │ ├── python.go │ │ └── python_test.go │ └── ruby │ │ └── ruby.go ├── cpe.go ├── cpe_test.go ├── match │ └── match.go ├── nvdtoolscache │ ├── cache.go │ ├── cache_test.go │ ├── db.go │ ├── load.go │ ├── load_test.go │ ├── singleton.go │ └── testdata │ │ ├── after │ │ └── small.json │ │ └── before │ │ └── small.json └── validation │ ├── all │ └── all.go │ ├── dotnetcoreruntime │ └── dotnetcoreruntime.go │ ├── java │ └── java.go │ ├── node │ └── node.go │ ├── python │ └── python.go │ ├── ruby │ └── ruby.go │ └── validation.go ├── database ├── database.go ├── metrics │ └── metrics.go ├── mock.go ├── models.go ├── namespace_mapping.go ├── options.go ├── pgsql │ ├── complex_test.go │ ├── feature.go │ ├── feature_test.go │ ├── image.go │ ├── keyvalue.go │ ├── keyvalue_test.go │ ├── language_vulnerability.go │ ├── language_vulnerability_test.go │ ├── layer.go │ ├── layer_test.go │ ├── lock.go │ ├── migrations │ │ ├── 00001_change_migrator.go │ │ ├── 00002_initial_schema.go │ │ ├── 00003_add_indexes.go │ │ ├── 00004_add_index_notification_deleted_at.go │ │ ├── 00005_ldfv_index.go │ │ ├── 00006_add_version_format.go │ │ ├── 00007_add_image.go │ │ ├── 00008_add_removed_components.go │ │ ├── 00009_distroless.go │ │ ├── 00010_rhelv2_vulns.go │ │ ├── 00011_rhelv2_scan.go │ │ ├── 00012_add_uncertified_rhel.go │ │ ├── 00013_add_vuln_title_rhel.go │ │ ├── 00014_add_layer_parent_hash.go │ │ ├── 00015_add_executables.go │ │ ├── 00016_add_shared_objects.go │ │ ├── 00017_rhel_add_shared_objects.go │ │ ├── 00018_rhelv2_vulns_v2.go │ │ ├── 00019_add_vuln_state_rhel.go │ │ ├── 00020_languagelayer_lineage_pk.go │ │ ├── 00021_rhel_layer_lineage.go │ │ └── migrations.go │ ├── namespace.go │ ├── namespace_test.go │ ├── pgsql.go │ ├── pgsql_test.go │ ├── queries.go │ ├── rhelv2_layer.go │ ├── rhelv2_layer_test.go │ ├── rhelv2_vulnerability.go │ ├── rhelv2_vulnerability_test.go │ ├── testdata │ │ └── data.sql │ ├── vulnerability.go │ └── vulnerability_test.go ├── severity.go ├── severity_test.go ├── string_map.go └── string_map_test.go ├── e2etests ├── components_test.go ├── empty.go ├── grpc_full_test.go ├── grpc_test.go ├── layer_test.go ├── node_scan_rhcos_test.go ├── node_scan_test.go ├── orchestrator_scan_test.go ├── sanity_test.go ├── testcase_test.go ├── testdata │ └── anchore_components.json ├── utils.go ├── utils_test.go └── vuln_test.go ├── ext ├── featurefmt │ ├── apk │ │ ├── apk.go │ │ ├── apk_test.go │ │ └── testdata │ │ │ ├── README.txt │ │ │ └── installed │ ├── dpkg │ │ ├── dpkg.go │ │ ├── dpkg_test.go │ │ └── testdata │ │ │ ├── README.txt │ │ │ ├── go.mod │ │ │ ├── libgcc1:amd64.list │ │ │ ├── libpam-modules-bin.list │ │ │ ├── libpam-runtime.list │ │ │ ├── pkg-source.list │ │ │ ├── pkg1:amd64.list │ │ │ ├── pkg2.list │ │ │ ├── status │ │ │ ├── statusd-base │ │ │ └── statusd-netbase │ ├── driver.go │ └── rpm │ │ ├── rpm.go │ │ ├── rpm_test.go │ │ └── testdata │ │ └── Packages ├── featurens │ ├── alpinerelease │ │ ├── alpinerelease.go │ │ └── alpinerelease_test.go │ ├── busybox │ │ ├── busybox.go │ │ └── busybox_test.go │ ├── driver.go │ ├── lsbrelease │ │ ├── lsbrelease.go │ │ └── lsbrelease_test.go │ ├── options.go │ ├── osrelease │ │ ├── osrelease.go │ │ └── osrelease_test.go │ ├── redhatrelease │ │ ├── redhatrelease.go │ │ └── redhatrelease_test.go │ └── util │ │ └── util.go ├── imagefmt │ ├── docker │ │ └── docker.go │ └── driver.go ├── kernelparser │ ├── all │ │ └── all.go │ ├── amzn │ │ ├── amzn.go │ │ └── amzn_test.go │ ├── cos │ │ ├── cos.go │ │ └── cos_test.go │ ├── debian │ │ ├── debian.go │ │ └── debian_test.go │ ├── errors.go │ ├── register.go │ ├── rhel │ │ ├── rhel.go │ │ └── rhel_test.go │ ├── ubuntu │ │ ├── ubuntu.go │ │ └── ubuntu_test.go │ └── windows │ │ └── windows.go ├── versionfmt │ ├── apk │ │ ├── apk.go │ │ └── apk_test.go │ ├── dpkg │ │ ├── parser.go │ │ └── parser_test.go │ ├── driver.go │ ├── language │ │ └── parser.go │ ├── rpm │ │ ├── parser.go │ │ └── parser_test.go │ └── test │ │ └── driver_test.go ├── vulnmdsrc │ ├── appender.go │ ├── nvd │ │ ├── json.go │ │ ├── nvd.go │ │ ├── nvd_test.go │ │ ├── singleton.go │ │ └── testdata │ │ │ ├── nvd_test.json │ │ │ └── nvd_test_incorrect_format.json │ ├── redhat │ │ ├── json.go │ │ ├── redhat.go │ │ ├── redhat_test.go │ │ ├── singleton.go │ │ └── testdata │ │ │ ├── redhat_test.json │ │ │ └── redhat_test_incorrect_format.json │ └── types │ │ └── types.go └── vulnsrc │ ├── all │ └── all.go │ ├── alpine │ ├── alpine.go │ ├── alpine_test.go │ └── testdata │ │ └── v34_main.yaml │ ├── amzn │ ├── amzn.go │ ├── amzn_test.go │ ├── repomd.go │ ├── testdata │ │ ├── amazon_linux_1_description_0.txt │ │ ├── amazon_linux_1_description_1.txt │ │ ├── amazon_linux_1_updateinfo.xml │ │ ├── amazon_linux_2_description_0.txt │ │ ├── amazon_linux_2_description_1.txt │ │ └── amazon_linux_2_updateinfo.xml │ └── updateinfo.go │ ├── debian │ ├── debian.go │ ├── debian_test.go │ └── testdata │ │ └── fetcher_debian_test.json │ ├── driver.go │ ├── manual │ └── manual.go │ ├── rhel │ ├── rhel.go │ ├── rhel_test.go │ └── testdata │ │ ├── fetcher_rhel_test.1.xml │ │ └── fetcher_rhel_test.2.xml │ ├── stackrox │ └── stackrox.go │ └── ubuntu │ ├── testdata │ └── fetcher_ubuntu_test.txt │ ├── ubuntu.go │ └── ubuntu_test.go ├── generated ├── .gitignore └── scanner │ └── api │ └── v1 │ ├── component.pb.go │ ├── component_vtproto.pb.go │ ├── empty.pb.go │ ├── empty_vtproto.pb.go │ ├── image.pb.go │ ├── image_scan_service.pb.go │ ├── image_scan_service.pb.gw.go │ ├── image_scan_service_grpc.pb.go │ ├── image_scan_service_vtproto.pb.go │ ├── image_vtproto.pb.go │ ├── metadata_service.pb.go │ ├── metadata_service.pb.gw.go │ ├── metadata_service_grpc.pb.go │ ├── metadata_service_vtproto.pb.go │ ├── node_inventory_service.pb.go │ ├── node_inventory_service.pb.gw.go │ ├── node_inventory_service_grpc.pb.go │ ├── node_inventory_service_vtproto.pb.go │ ├── node_scan_service.pb.go │ ├── node_scan_service.pb.gw.go │ ├── node_scan_service_grpc.pb.go │ ├── node_scan_service_vtproto.pb.go │ ├── note.pb.go │ ├── orchestrator_scan_service.pb.go │ ├── orchestrator_scan_service.pb.gw.go │ ├── orchestrator_scan_service_grpc.pb.go │ ├── orchestrator_scan_service_vtproto.pb.go │ ├── ping_service.pb.go │ ├── ping_service.pb.gw.go │ ├── ping_service_grpc.pb.go │ ├── ping_service_vtproto.pb.go │ ├── vulnerability.pb.go │ └── vulnerability_vtproto.pb.go ├── go.mod ├── go.sum ├── image ├── README.md ├── bin │ └── .gitignore ├── db │ ├── dump │ │ └── .gitignore │ ├── pg_hba.conf │ ├── postgresql.conf │ └── rhel │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.slim │ │ ├── create-bundle.sh │ │ ├── konflux.Dockerfile │ │ ├── scripts │ │ ├── docker-entrypoint.sh │ │ └── download.sh │ │ └── signatures │ │ └── PGDG-RPM-GPG-KEY-RHEL ├── scanner │ ├── bin │ │ └── .gitignore │ ├── dump │ │ ├── .gitignore │ │ ├── README.md │ │ └── genesis_manifests.json │ ├── rhel │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.slim │ │ ├── create-bundle.sh │ │ └── konflux.Dockerfile │ └── scripts │ │ ├── entrypoint.sh │ │ ├── import-additional-cas │ │ ├── restore-all-dir-contents │ │ ├── save-dir-contents │ │ └── trust-root-ca └── vulnerabilities │ └── Dockerfile ├── img └── Logo-Red_Hat-Certified_Technology-Vulnerability_Scanner-A-Red-RGB.png ├── istio └── cache │ ├── cache.go │ ├── cache_test.go │ ├── db.go │ ├── load.go │ ├── singleton.go │ └── testdata │ ├── after │ └── ISTIO-SECURITY-2022-123.yaml │ └── before │ └── ISTIO-SECURITY-2022-123.yaml ├── k8s └── cache │ ├── cache.go │ ├── cache_test.go │ ├── db.go │ ├── load.go │ ├── singleton.go │ └── testdata │ ├── after │ ├── CVE-2020-1234.yaml │ ├── CVE-2020-1236.yaml │ └── CVE-2020-1238.yaml │ └── before │ └── CVE-2020-1234.yaml ├── localdev └── main.go ├── make └── protogen.mk ├── pkg ├── analyzer │ ├── analyzer.go │ ├── analyzertest │ │ └── fake_file.go │ ├── detection │ │ ├── detection.go │ │ ├── detection_test.go │ │ └── errors.go │ ├── dotnetcoreruntime │ │ ├── analyzer.go │ │ ├── analyzer_test.go │ │ └── testdata │ │ │ └── deps.json │ ├── gem │ │ ├── analyzer.go │ │ ├── analyzer_test.go │ │ ├── parse_gem_spec.go │ │ └── parse_gem_spec_test.go │ ├── internal │ │ └── common │ │ │ └── has_name_version.go │ ├── java │ │ ├── analyzer.go │ │ ├── java.go │ │ ├── parse_manifest_test.go │ │ └── parse_manifests.go │ ├── nodes │ │ ├── node.go │ │ └── node_test.go │ ├── npm │ │ ├── analyzer.go │ │ └── parse_package_json.go │ └── python │ │ ├── analyzer.go │ │ └── parse_metadata.go ├── archop │ ├── archop.go │ └── archop_string.go ├── bolthelper │ └── bolt.go ├── cache │ └── cache.go ├── clairify │ ├── clair │ │ ├── clair.go │ │ └── clair_test.go │ ├── client │ │ ├── client.go │ │ ├── client_test.go │ │ ├── errors.go │ │ └── metadata │ │ │ └── metadata_keys.go │ ├── fixtures │ │ └── fixtures.go │ ├── metrics │ │ ├── server.go │ │ └── throttle.go │ ├── server │ │ ├── middleware │ │ │ ├── log.go │ │ │ ├── slim_mode.go │ │ │ └── verify_peer_certs.go │ │ ├── server.go │ │ └── server_test.go │ └── types │ │ ├── types.go │ │ └── types_test.go ├── commonerr │ └── errors.go ├── component │ ├── component.go │ ├── sourcetype_string.go │ ├── type.go │ └── valid.go ├── cpe │ ├── attribute_string.go │ ├── bind.go │ ├── cpe.go │ ├── json.go │ ├── sql.go │ ├── unbind.go │ ├── valuekind_string.go │ ├── wfn.go │ └── wfn_test.go ├── cpeutils │ ├── utils.go │ └── utils_test.go ├── cvss │ └── util.go ├── elf │ ├── elf.go │ ├── elf_test.go │ └── testdata │ │ ├── README.md │ │ ├── elf_exec │ │ └── macho_exec ├── env │ ├── booleansetting.go │ ├── durationsetting.go │ ├── durationsetting_options.go │ ├── integersetting.go │ ├── list.go │ └── setting.go ├── features │ ├── features.go │ └── list.go ├── formatter │ └── formatter.go ├── fsutil │ ├── fileinfo │ │ ├── mock │ │ │ ├── mock.go │ │ │ └── options.go │ │ └── util.go │ ├── fsutil.go │ └── fsutil_test.go ├── httputil │ └── httputil.go ├── ioutils │ ├── lazy_reader.go │ ├── lazy_reader_disk.go │ └── lazy_reader_disk_test.go ├── istioutil │ └── util.go ├── matcher │ ├── matcher.go │ └── matcher_test.go ├── metrics │ ├── extractfiles.go │ ├── features.go │ └── tar.go ├── mtls │ ├── mtls.go │ ├── verifier.go │ └── verifier_test.go ├── nodeinventory │ ├── caching_inventorizer.go │ ├── caching_inventorizer_test.go │ ├── inventorizer.go │ └── inventorizer_test.go ├── nvd │ └── link.go ├── osrelease │ ├── osrelease.go │ └── osrelease_test.go ├── ovalutil │ └── test.go ├── repo2cpe │ ├── mapping.go │ ├── mapping_test.go │ ├── singleton.go │ └── testdata │ │ └── repository-to-cpe.json ├── rhel │ ├── layer_name.go │ └── pulp │ │ ├── manifest.go │ │ ├── manifest_test.go │ │ └── testdata │ │ └── PULP_MANIFEST ├── rhelv2 │ ├── fetcher.go │ ├── ovalutil │ │ ├── rpm.go │ │ ├── rpm_test.go │ │ └── testdata │ │ │ └── oval.xml │ ├── parser.go │ ├── rhelv2.go │ └── rpm │ │ ├── bench_test.go │ │ ├── rpm.go │ │ ├── rpm_language_analyzer.go │ │ ├── rpm_test.go │ │ └── testdata │ │ ├── Packages │ │ ├── repository-to-cpe.json │ │ ├── rpmdb.sqlite │ │ └── test.json ├── rpm │ ├── database.go │ └── database_test.go ├── scan │ ├── downloadCloser.go │ └── scan.go ├── stringhelpers │ └── contains.go ├── tarutil │ ├── layer_files.go │ ├── layer_files_test.go │ ├── tarutil.go │ ├── tarutil_test.go │ └── testdata │ │ ├── symlink.tar.gz │ │ ├── utils_test.tar │ │ ├── utils_test.tar.bz2 │ │ ├── utils_test.tar.gz │ │ └── utils_test.tar.xz ├── types │ ├── types.go │ └── types_test.go ├── updater │ ├── config.go │ ├── fetcher.go │ ├── genesis_manifests.go │ ├── genesis_manifests_test.go │ ├── slim_updater.go │ └── updater.go ├── version │ └── version.go ├── vulndump │ ├── batch_loader.go │ ├── batch_loader_test.go │ ├── loader.go │ ├── loader_test.go │ ├── well_known_names.go │ └── write.go ├── vulnkey │ └── key.go ├── vulnloader │ ├── all │ │ └── all.go │ ├── driver.go │ ├── istioloader │ │ ├── loader.go │ │ └── yaml.go │ ├── k8sloader │ │ ├── loader.go │ │ └── yaml.go │ ├── nvdloader │ │ ├── convert.go │ │ ├── enricher.go │ │ ├── json.go │ │ ├── loader.go │ │ ├── loader_legacy.go │ │ ├── manual.go │ │ └── nvdloader_easyjson.go │ └── redhatloader │ │ └── loader.go ├── wellknowndirnames │ └── dir.go ├── wellknownkeys │ └── list.go ├── wellknownnamespaces │ ├── set.go │ └── util.go ├── whiteout │ └── pattern.go └── ziputil │ ├── open.go │ ├── open_test.go │ └── testdata │ └── test.zip ├── proto └── scanner │ └── api │ └── v1 │ ├── component.proto │ ├── empty.proto │ ├── image.proto │ ├── image_scan_service.proto │ ├── metadata_service.proto │ ├── node_inventory_service.proto │ ├── node_scan_service.proto │ ├── note.proto │ ├── orchestrator_scan_service.proto │ ├── ping_service.proto │ └── vulnerability.proto ├── rendered-chart └── .gitignore ├── rpms.in.yaml ├── rpms.lock.yaml ├── rpms.rhel.repo ├── scale └── scale.go ├── scripts ├── cert │ ├── README.md │ ├── csr.json │ └── gen-cert.sh ├── ci │ ├── cleanup-deployment.sh │ ├── collect-scanner-metrics.sh │ ├── collect-service-logs.sh │ ├── deploy.sh │ ├── gate-jobs-config.json │ ├── gcp.sh │ ├── gke.sh │ ├── jobs │ │ ├── db-integration-tests.sh │ │ ├── diff-dumps.sh │ │ ├── e2e_tests.py │ │ ├── e2etests │ │ │ ├── e2e-tests.sh │ │ │ ├── scale-tests.sh │ │ │ └── slim-e2e-tests.sh │ │ ├── push-images.sh │ │ ├── sanity-check-vuln-updates.sh │ │ ├── scale_tests.py │ │ ├── slim_e2e_tests.py │ │ ├── store-db-dump.sh │ │ ├── store-genesis-dump.sh │ │ ├── style-checks.sh │ │ ├── unit-tests.sh │ │ ├── upload-db-dump.sh │ │ ├── upload-dumps-for-downstream.sh │ │ └── upload-dumps-for-embedding.sh │ ├── lib.sh │ ├── logcheck │ │ ├── allowlist-patterns │ │ ├── blocklist-patterns │ │ ├── check-logs.sh │ │ ├── check-restart-logs.sh │ │ └── check.sh │ ├── postgres.sh │ ├── push-as-multiarch-manifest-list.sh │ └── store-artifacts.sh ├── docker-build.sh ├── generate-junit-reports.sh ├── go-get-version.sh ├── lib.sh └── node │ ├── README.md │ ├── dockerfile │ └── generate-rpm-specs.sh ├── singletons ├── analyzers │ └── analyzers.go └── requiredfilenames │ ├── matcher.go │ └── matcher_test.go ├── status.sh ├── testdata ├── DistUpgrade │ ├── blank.tar.gz │ ├── jessie.tar.gz │ └── wheezy.tar.gz └── NodeScanning │ └── rhcos4.12-minimal.tar.gz ├── testimages └── package-removal │ ├── Dockerfile.alpine │ ├── Dockerfile.centos │ ├── Dockerfile.debian │ ├── Dockerfile.rhel │ ├── Dockerfile.ubuntu │ └── build-and-push.sh ├── third_party └── googleapis │ └── google │ └── api │ ├── annotations.proto │ └── http.proto ├── tools ├── allowed-large-files ├── bin │ └── .gitignore ├── check-newlines.sh ├── check-service-protos │ └── run.sh ├── clean_autogen_protos.py ├── detect-large-files.sh ├── fix-blanks.sh ├── import_validate.py ├── linters │ ├── go.mod │ ├── go.sum │ ├── tools-import.go │ └── tools.go ├── local-nodescanner │ ├── Dockerfile │ ├── README.md │ └── main.go ├── test │ ├── go.mod │ ├── go.sum │ └── tools-import.go ├── tools-import.go └── tools.go ├── worker.go └── worker_test.go /.containerignore: -------------------------------------------------------------------------------- 1 | # Konflux uses buildah which first looks up `.containerignore` then, if absent, `.dockerignore`. 2 | # Having this file enables us to have different exclusion list for Konflux builds than in GHA/OSCI. 3 | 4 | # This file is empty because all the items that are ignored in GHA/OSCI need to be present for Konflux builds: 5 | # * `.git` is needed so we can bake build information into the binary. 6 | # * `.gitignore`, `image/bin/` and `bin` are required to avoid a dirty version. 7 | 8 | # If content ever needs to be added here, be sure to borrow the check-generated.sh template from the 9 | # stackrox/stackrox repo to ensure .dockerfile and .containerignore stay in sync. 10 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # NOTE: if adding items here, please implement the check-generated.sh script 2 | # from stackrox/stackrox to keep this file and .containeringore in sync 3 | 4 | /.git/ 5 | /bin/ 6 | /image/bin/ 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [{Makefile,**.mk}] 2 | indent_style = tab 3 | 4 | [**.proto] 5 | indent_style = space 6 | indent_size = 4 7 | 8 | [{*.yaml,*.yml}] 9 | # ij_ settings meaning can be mapped from https://www.jetbrains.com/help/idea/code-style-yaml.html 10 | ij_yaml_indent_sequence_value = false 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pb.go linguist-generated=true 2 | *.pb.gw.go linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # For further documentation on CODEOWNERS, visit 2 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#about-code-owners 3 | # This will automatically assign a team / people as reviewers for PRs based on the files changed within the PR. 4 | 5 | * @stackrox/scanner 6 | 7 | # The RHTAP maintainers for ACS review all changes related to the Konflux (f.k.a. RHTAP) pipelines, such as new 8 | # pipelines, parameter changes or automated task updates as well as Dockerfile updates. 9 | **/konflux.*Dockerfile @stackrox/rhtap-maintainers 10 | /.konflux/ @stackrox/rhtap-maintainers 11 | /.tekton/ @stackrox/rhtap-maintainers 12 | rpms.* @stackrox/rhtap-maintainers 13 | -------------------------------------------------------------------------------- /.github/actions/cache-go-dependencies/action.yaml: -------------------------------------------------------------------------------- 1 | name: Cache Go Dependencies 2 | description: Cache Go Dependencies 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Determine Go cache paths 7 | id: cache-paths 8 | run: | 9 | echo "GOCACHE=$(go env GOCACHE)" >> "$GITHUB_OUTPUT" 10 | echo "GOMODCACHE=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT" 11 | shell: bash 12 | 13 | - name: Cache Go Dependencies 14 | uses: actions/cache@v4 15 | with: 16 | path: | 17 | ${{ steps.cache-paths.outputs.GOMODCACHE }} 18 | key: go-mod-v1-${{ hashFiles('**/go.sum') }} 19 | 20 | - name: Cache Go Build 21 | uses: actions/cache@v4 22 | with: 23 | path: | 24 | ${{ steps.cache-paths.outputs.GOCACHE }} 25 | key: go-build-v1-${{ github.job }}-${{ hashFiles('**/go.sum') }} 26 | 27 | - name: Download Go modules 28 | run: make deps --always-make 29 | shell: bash 30 | -------------------------------------------------------------------------------- /.github/actions/handle-tagged-build/action.yaml: -------------------------------------------------------------------------------- 1 | name: Handle tagged build 2 | description: Handle tagged build 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Handle tagged build 7 | run: | 8 | source ./scripts/ci/lib.sh 9 | handle_gha_tagged_build 10 | shell: bash 11 | -------------------------------------------------------------------------------- /.github/actions/job-preamble/action.yaml: -------------------------------------------------------------------------------- 1 | name: Job Preamble 2 | description: Common steps for most jobs 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Recover docker image cache space 7 | run: | 8 | df --si / 9 | docker system prune --force --all 10 | df --si / 11 | shell: bash 12 | 13 | - name: Ignore dubious repository ownership 14 | run: | 15 | # Prevent fatal error "detected dubious ownership in repository" from recent git. 16 | git config --global --add safe.directory "$(pwd)" 17 | shell: bash 18 | -------------------------------------------------------------------------------- /.github/workflows/add-new-pr-to-oss-triaging.yml: -------------------------------------------------------------------------------- 1 | name: Add any new Pull Request to OSS Triaging project 2 | on: 3 | pull_request_target: 4 | types: [opened, reopened] 5 | 6 | env: 7 | EXTERNAL_PR_LABEL: external-contributor 8 | 9 | jobs: 10 | check-pr-if-external: 11 | name: Add external label to pull request if outside StackRox 12 | runs-on: ubuntu-latest 13 | env: 14 | GH_TOKEN: ${{ github.token }} 15 | BASE_REPO: ${{ github.repository }} 16 | HEAD_REPO: ${{ github.event.pull_request.head.user.login }}/${{ github.event.pull_request.head.repo.name }} 17 | steps: 18 | - name: Check out code 19 | uses: actions/checkout@v4 20 | - id: check-external-pr 21 | run: | 22 | set -uo pipefail 23 | if [[ $BASE_REPO != $HEAD_REPO ]]; then 24 | gh pr edit \ 25 | ${{ github.event.pull_request.number }} \ 26 | --add-label ${EXTERNAL_PR_LABEL} 27 | fi 28 | -------------------------------------------------------------------------------- /.github/workflows/sanity-check-vuln-updates.yaml: -------------------------------------------------------------------------------- 1 | name: Vulnerability updates sanity check 2 | on: 3 | schedule: 4 | - cron: '5 0,4,8,12,16,20 * * *' 5 | 6 | jobs: 7 | sanity-check-vuln-updates: 8 | env: 9 | GOOGLE_SA_STACKROX_HUB_VULN_DUMP_UPLOADER: ${{ secrets.GOOGLE_SA_STACKROX_HUB_VULN_DUMP_UPLOADER }} 10 | SLACK_WEBHOOK_ONCALL: ${{ secrets.SLACK_ONCALL_SCANNER_WEBHOOK }} 11 | runs-on: ubuntu-latest 12 | container: 13 | image: quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | ref: master 20 | 21 | - uses: ./.github/actions/job-preamble 22 | 23 | - name: sanity-check-vuln-updates 24 | run: ./scripts/ci/jobs/sanity-check-vuln-updates.sh 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | .vscode 4 | 5 | # Mac OS hidden file 6 | .DS_Store 7 | 8 | # Test output 9 | /junit-reports/ 10 | 11 | # Makefile cruft 12 | deps 13 | proto-generated-srcs 14 | protoc-* 15 | report.xml 16 | test.log 17 | /.gobin/ 18 | /.proto/ 19 | /genesis-dump/ 20 | /test-output/ 21 | 22 | # nohup output when running port-forwards 23 | nohup.out 24 | 25 | # Vim swap files 26 | *.swp 27 | 28 | # Go mod 29 | vendor 30 | -------------------------------------------------------------------------------- /.konflux/README.md: -------------------------------------------------------------------------------- 1 | # .konflux 2 | 3 | This directory is for files supporting ACS builds in Konflux. 4 | There's no Konflux convention to use it. Rather, we organize our scripts and tools here ourselves. 5 | -------------------------------------------------------------------------------- /.konflux/scanner-data/.gitignore: -------------------------------------------------------------------------------- 1 | # scanner-data is for blobs downloaded and included in Scanner V2 containers during build. 2 | * 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /.openshift-ci/Dockerfile.build_root: -------------------------------------------------------------------------------- 1 | # Changes to this file are not validated automatically by CI. That is because 2 | # the CI as defined in openshift/release runs against HEAD and uses the version 3 | # of this file found there. 4 | 5 | # In order to validate a change to this file i.e. a new version of the test environment: 6 | # - make the change on a stackrox/scanner PR (do not use / in the branch name as it is not supported in openshift/release) 7 | # - open a PR in openshift/release (this is just for test. mark the PR with `/hold` and `/uncc` autoassigned reviewers to reduce noise) 8 | # - duplicate the main branch CI workflow to a workflow that tests the stackrox/scanner PR branch 9 | # - run openshift/release automation to generate the prow config 10 | # - `make update` and commit the results 11 | # - run `/test pj-rehearse-max` on the openshift/release PR to validate the change 12 | 13 | FROM quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 14 | -------------------------------------------------------------------------------- /.openshift-ci/build/Dockerfile.build-bundle: -------------------------------------------------------------------------------- 1 | FROM quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 2 | 3 | COPY . /go/src/github.com/stackrox/scanner 4 | WORKDIR /go/src/github.com/stackrox/scanner 5 | 6 | RUN ./.openshift-ci/build/build-bundle.sh 7 | -------------------------------------------------------------------------------- /.openshift-ci/build/Dockerfile.build-db-bundle: -------------------------------------------------------------------------------- 1 | FROM quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 2 | 3 | COPY . /go/src/github.com/stackrox/scanner 4 | WORKDIR /go/src/github.com/stackrox/scanner 5 | 6 | RUN ./.openshift-ci/build/build-db-bundle.sh 7 | -------------------------------------------------------------------------------- /.openshift-ci/build/Dockerfile.generate-db-dump: -------------------------------------------------------------------------------- 1 | FROM quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 2 | 3 | COPY . /go/src/github.com/stackrox/scanner 4 | WORKDIR /go/src/github.com/stackrox/scanner 5 | 6 | RUN ./.openshift-ci/build/generate-db-dump.sh 7 | -------------------------------------------------------------------------------- /.openshift-ci/build/Dockerfile.generate-genesis-dump: -------------------------------------------------------------------------------- 1 | FROM quay.io/stackrox-io/apollo-ci:scanner-test-0.4.8 2 | 3 | COPY . /go/src/github.com/stackrox/scanner 4 | WORKDIR /go/src/github.com/stackrox/scanner 5 | 6 | RUN ./.openshift-ci/build/generate-genesis-dump.sh 7 | -------------------------------------------------------------------------------- /.openshift-ci/build/build-db-bundle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Execute the build steps required to create the scanner-db image's bundle.tar.gz. 4 | # 5 | # Adapted from https://github.com/stackrox/stackrox/blob/master/.openshift-ci/build/build-central-db-bundle.sh 6 | 7 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 8 | # shellcheck source=../../scripts/ci/gcp.sh 9 | source "$ROOT/scripts/ci/gcp.sh" 10 | # shellcheck source=../../scripts/ci/lib.sh 11 | source "$ROOT/scripts/ci/lib.sh" 12 | 13 | set -euo pipefail 14 | 15 | openshift_ci_mods 16 | 17 | get_db_dump() { 18 | info "Retrieving DB dump" 19 | 20 | ls -lrt /tmp/postgres || info "No local DB dump" 21 | 22 | if is_in_PR_context && ! pr_has_label "generate-dumps-on-pr"; then 23 | info "Label generate-dumps-on-pr not set. Pulling dumps from GCS bucket" 24 | gsutil cp gs://stackrox-scanner-ci-vuln-dump/pg-definitions.sql.gz "$ROOT/image/db/dump/definitions.sql.gz" 25 | else 26 | cp /tmp/postgres/pg-definitions.sql.gz "$ROOT/image/db/dump/definitions.sql.gz" 27 | fi 28 | } 29 | 30 | build_db_bundle() { 31 | get_db_dump 32 | 33 | info "Creating scanner-db bundle.tar.gz" 34 | "$ROOT/image/db/rhel/create-bundle.sh" "$ROOT/image/db" "$ROOT/image/db/rhel" 35 | } 36 | 37 | build_db_bundle 38 | -------------------------------------------------------------------------------- /.openshift-ci/build/generate-db-dump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 4 | # shellcheck source=../../scripts/ci/lib.sh 5 | source "$ROOT/scripts/ci/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | openshift_ci_mods 10 | 11 | gate_job generate-db-dump 12 | 13 | generate_db_dump() { 14 | info "Generating DB dump" 15 | 16 | groupadd -g 1001 pg 17 | adduser pg -u 1001 -g 1001 -d /var/lib/postgresql -s /bin/sh 18 | 19 | # The PATH is not completely preserved, so set the PATH here to ensure postgres-related commands can be found. 20 | runuser -l pg -c "PATH=$PATH $ROOT/scripts/ci/postgres.sh start_postgres" 21 | 22 | "$ROOT/bin/updater" load-dump --postgres-host 127.0.0.1 --postgres-port 5432 --dump-file /tmp/genesis-dump/genesis-dump.zip 23 | 24 | mkdir /tmp/postgres 25 | pg_dump -U postgres postgres://127.0.0.1:5432 > /tmp/postgres/pg-definitions.sql 26 | ls -lrt /tmp/postgres 27 | gzip --best /tmp/postgres/pg-definitions.sql 28 | ls -lrt /tmp/postgres 29 | } 30 | 31 | generate_db_dump "$*" 32 | -------------------------------------------------------------------------------- /.openshift-ci/build/generate-genesis-dump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Execute all steps required to generate the genesis dump 4 | 5 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 6 | # shellcheck source=../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | 9 | set -euo pipefail 10 | 11 | openshift_ci_mods 12 | 13 | gate_job generate-genesis-dump 14 | 15 | generate_genesis_dump() { 16 | info "Building updater" 17 | make build-updater 18 | 19 | info "Generating genesis dump" 20 | mkdir -p /tmp/genesis-dump 21 | "$ROOT/bin/updater" generate-dump --out-file /tmp/genesis-dump/genesis-dump.zip 22 | ls -lrt /tmp/genesis-dump 23 | 24 | info "Printing some stats" 25 | "$ROOT/bin/updater" print-stats /tmp/genesis-dump/genesis-dump.zip 26 | 27 | info "Extracting dumps" 28 | mkdir -p /tmp/vuln-dump 29 | zip /tmp/genesis-dump/genesis-dump.zip 'nvd/*' --copy --out /tmp/vuln-dump/nvd-definitions.zip 30 | zip /tmp/genesis-dump/genesis-dump.zip 'k8s/*' --copy --out /tmp/vuln-dump/k8s-definitions.zip 31 | zip /tmp/genesis-dump/genesis-dump.zip 'istio/*' --copy --out /tmp/vuln-dump/istio-definitions.zip 32 | zip /tmp/genesis-dump/genesis-dump.zip 'rhelv2/repository-to-cpe.json' --copy --out /tmp/vuln-dump/repo2cpe.zip 33 | } 34 | 35 | generate_genesis_dump 36 | -------------------------------------------------------------------------------- /.openshift-ci/common.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | ''' 4 | Copied from https://github.com/stackrox/stackrox/blob/master/.openshift-ci/common.py 5 | ''' 6 | 7 | def popen_graceful_kill(cmd): 8 | cmd.terminate() 9 | try: 10 | cmd.wait(5) 11 | except subprocess.TimeoutExpired as err: 12 | cmd.kill() 13 | cmd.wait(5) 14 | raise err 15 | -------------------------------------------------------------------------------- /.ossls.yml: -------------------------------------------------------------------------------- 1 | gomod: 2 | mod-file: go.mod 3 | 4 | patterns: 5 | - "*AUTHOR*" 6 | - "*COPYING*" 7 | - "*LICENSE*" 8 | - "*LICENCE*" 9 | - "*NOTICE*" 10 | - '~^.*(?i:notice|licen[cs]e).*\.(?i:txt|md)$' 11 | - "package.json" 12 | 13 | excludePatterns: 14 | - "*.go" 15 | - "*.js" 16 | - "*.ts" 17 | - "*.sh" 18 | -------------------------------------------------------------------------------- /.syft.yaml: -------------------------------------------------------------------------------- 1 | # Konflux uses Syft to generate container SBOMs. 2 | # Syft config docs https://github.com/anchore/syft/wiki/configuration 3 | 4 | # Here we exclude files checked in this repo for testing purposes from being parsed and merged into SBOM. 5 | # This is in particular to prevent rpm packages from `./pkg/rhelv2/rpm/testdata/rpmdb.sqlite` and similar to be included 6 | # in the SBOMs of built containers. 7 | exclude: 8 | - ./**/testdata/** 9 | -------------------------------------------------------------------------------- /BUILD_IMAGE_VERSION: -------------------------------------------------------------------------------- 1 | scanner-build-0.4.8 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Stackrox code of conduct can be found [here](https://stackrox.io/code-conduct). 4 | -------------------------------------------------------------------------------- /api/grpc/logger.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | ) 7 | 8 | // httpErrorLogger implements io.Writer interface. It is used to control 9 | // error messages coming from http server which can be logged. 10 | type httpErrorLogger struct { 11 | } 12 | 13 | // Write suppresses EOF error messages 14 | func (l httpErrorLogger) Write(p []byte) (n int, err error) { 15 | if !bytes.Contains(p, []byte("EOF")) { 16 | return os.Stderr.Write(p) 17 | } 18 | return len(p), nil 19 | } 20 | -------------------------------------------------------------------------------- /api/grpc/logging_interceptor.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/sirupsen/logrus" 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | const pingMethod = `/scannerV1.PingService/Ping` 13 | 14 | func loggingUnaryServerInterceptor() grpc.UnaryServerInterceptor { 15 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 16 | if info.FullMethod == pingMethod { 17 | // Don't bother logging pings. 18 | return handler(ctx, req) 19 | } 20 | 21 | logrus.WithFields(map[string]interface{}{ 22 | "URI": info.FullMethod, 23 | }).Infof("Received gRPC request") 24 | 25 | start := time.Now() 26 | resp, err := handler(ctx, req) 27 | duration := fmt.Sprintf("%f seconds", time.Since(start).Seconds()) 28 | 29 | logrus.WithFields(map[string]interface{}{ 30 | "URI": info.FullMethod, 31 | "Duration": duration, 32 | }).Infof("Finished gRPC request") 33 | 34 | return resp, err 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/grpc/slim_mode_interceptor.go: -------------------------------------------------------------------------------- 1 | // Any changes to this file should be considered for its counterpart: 2 | // pkg/clairify/server/middleware/slim_mode.go 3 | 4 | package grpc 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/stackrox/rox/pkg/set" 10 | "github.com/stackrox/scanner/pkg/env" 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/codes" 13 | "google.golang.org/grpc/status" 14 | ) 15 | 16 | var ( 17 | // Method name(s) taken from the respective generated pb.go file(s). 18 | slimModeMethodsAllowlist = set.NewFrozenStringSet( 19 | "/scannerV1.PingService/Ping", 20 | "/scannerV1.ImageScanService/GetImageComponents", 21 | ) 22 | ) 23 | 24 | func slimModeUnaryServerInterceptor() grpc.UnaryServerInterceptor { 25 | slimMode := env.SlimMode.Enabled() 26 | 27 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 28 | if slimMode && !slimModeMethodsAllowlist.Contains(info.FullMethod) { 29 | return nil, status.Error(codes.NotFound, "request not available in slim-mode") 30 | } 31 | 32 | return handler(ctx, req) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/v1/common/feature_key_set.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/stackrox/scanner/ext/featurefmt" 5 | ) 6 | 7 | // FeatureKeySet contains a set of feature keys 8 | type FeatureKeySet map[featurefmt.PackageKey]struct{} 9 | 10 | // Merge adds all feature keys from the other feature set 11 | func (f *FeatureKeySet) Merge(other FeatureKeySet) { 12 | if *f == nil && len(other) > 0 { 13 | *f = make(FeatureKeySet, len(other)) 14 | } 15 | for key := range other { 16 | (*f)[key] = struct{}{} 17 | } 18 | } 19 | 20 | // Add adds a feature key to this feature key set. 21 | func (f *FeatureKeySet) Add(featureKey featurefmt.PackageKey) { 22 | if *f == nil { 23 | *f = make(FeatureKeySet) 24 | } 25 | (*f)[featureKey] = struct{}{} 26 | } 27 | -------------------------------------------------------------------------------- /api/v1/common/feature_key_set_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stackrox/scanner/ext/featurefmt" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFeatureKeySet(t *testing.T) { 11 | var a, b FeatureKeySet 12 | a.Merge(b) 13 | assert.Nil(t, a) 14 | 15 | f0 := featurefmt.PackageKey{Name: "a", Version: "v1"} 16 | f1 := featurefmt.PackageKey{Name: "b", Version: "v2"} 17 | f2 := featurefmt.PackageKey{Name: "c", Version: "v3"} 18 | 19 | a = FeatureKeySet{featurefmt.PackageKey{Name: "a", Version: "v1"}: {}} 20 | b.Merge(a) 21 | assert.Equal(t, a, b) 22 | a.Add(f1) 23 | b.Add(f2) 24 | a.Merge(b) 25 | assert.Len(t, a, 3) 26 | assert.Contains(t, a, f1, f2, f0) 27 | } 28 | -------------------------------------------------------------------------------- /api/v1/convert/k8s.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "github.com/stackrox/k8s-cves/pkg/validation" 6 | "github.com/stackrox/rox/pkg/stringutils" 7 | v1 "github.com/stackrox/scanner/generated/scanner/api/v1" 8 | "github.com/stackrox/scanner/pkg/types" 9 | ) 10 | 11 | // K8sVulnerabilities converts k8s cve schema to vulnerability. 12 | func K8sVulnerabilities(version string, k8sVulns []*validation.CVESchema) ([]*v1.Vulnerability, error) { 13 | vulns := make([]*v1.Vulnerability, 0, len(k8sVulns)) 14 | for _, v := range k8sVulns { 15 | m, err := types.ConvertMetadataFromK8s(v) 16 | if err != nil { 17 | log.Errorf("unable to convert metadata for %s: %v", v.CVE, err) 18 | continue 19 | } 20 | if m.IsNilOrEmpty() { 21 | log.Warnf("nil or empty metadata for %s", v.CVE) 22 | continue 23 | } 24 | 25 | link := stringutils.OrDefault(v.IssueURL, v.URL) 26 | fixedBy, err := GetFixedBy(version, v) 27 | if err != nil { 28 | log.Errorf("unable to get fixedBy for %s: %v", v.CVE, err) 29 | continue 30 | } 31 | vulns = append(vulns, &v1.Vulnerability{ 32 | Name: v.CVE, 33 | Description: v.Description, 34 | Link: link, 35 | MetadataV2: Metadata(m), 36 | FixedBy: fixedBy, 37 | Severity: string(DatabaseSeverityToSeverity(m.GetDatabaseSeverity())), 38 | }) 39 | } 40 | return vulns, nil 41 | } 42 | -------------------------------------------------------------------------------- /api/v1/convert/severity.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import "github.com/stackrox/scanner/database" 4 | 5 | // Severity is the uniform severity returned through the API 6 | type Severity string 7 | 8 | // Severity settings for vulnerabilities 9 | const ( 10 | UnknownSeverity Severity = "Unknown" 11 | LowSeverity Severity = "Low" 12 | ModerateSeverity Severity = "Moderate" 13 | ImportantSeverity Severity = "Important" 14 | CriticalSeverity Severity = "Critical" 15 | ) 16 | 17 | // DatabaseSeverityToSeverity converts a database.Severity into a Severity. 18 | func DatabaseSeverityToSeverity(severity database.Severity) Severity { 19 | switch severity { 20 | case database.UnknownSeverity: 21 | return UnknownSeverity 22 | case database.NegligibleSeverity, database.LowSeverity: 23 | return LowSeverity 24 | case database.MediumSeverity: 25 | return ModerateSeverity 26 | case database.HighSeverity: 27 | return ImportantSeverity 28 | case database.CriticalSeverity, database.Defcon1Severity: 29 | return CriticalSeverity 30 | } 31 | return LowSeverity 32 | } 33 | -------------------------------------------------------------------------------- /api/v1/convert/version.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/hashicorp/go-version" 7 | "github.com/pkg/errors" 8 | "github.com/stackrox/k8s-cves/pkg/validation" 9 | ) 10 | 11 | var ( 12 | semverPattern = regexp.MustCompile(`(?:v)?([0-9]+\.[0-9]+\.[0-9]+)(?:[-+]?.*)`) 13 | ) 14 | 15 | // TruncateVersion converts the given version into a semantic version x.y.z. 16 | // Returns empty string "" 17 | func TruncateVersion(v string) (string, error) { 18 | vs := semverPattern.FindStringSubmatch(v) 19 | if len(vs) == 2 { 20 | return vs[1], nil 21 | } 22 | return "", errors.Errorf("unsupported version: %s", v) 23 | } 24 | 25 | // GetFixedBy gets the fixed-by version for vStr in vuln. 26 | func GetFixedBy(vStr string, vuln *validation.CVESchema) (string, error) { 27 | v, err := version.NewVersion(vStr) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | for _, affected := range vuln.Affected { 33 | constraint, err := version.NewConstraint(affected.Range) 34 | if err != nil { 35 | return "", err 36 | } 37 | if constraint.Check(v) { 38 | return affected.FixedBy, nil 39 | } 40 | } 41 | 42 | return "", nil 43 | } 44 | -------------------------------------------------------------------------------- /api/v1/imagescan/types.go: -------------------------------------------------------------------------------- 1 | package imagescan 2 | 3 | import v1 "github.com/stackrox/scanner/generated/scanner/api/v1" 4 | 5 | // imageRequest is an interface wrapper for a v1 image-related request. 6 | type imageRequest interface { 7 | GetImageSpec() *v1.ImageSpec 8 | GetUncertifiedRHEL() bool 9 | } 10 | 11 | // imageReq is an implementation of imageRequest. 12 | type imageReq struct { 13 | imageSpec *v1.ImageSpec 14 | uncertifiedRHEL bool 15 | } 16 | 17 | func (i *imageReq) GetImageSpec() *v1.ImageSpec { 18 | return i.imageSpec 19 | } 20 | 21 | func (i *imageReq) GetUncertifiedRHEL() bool { 22 | return i.uncertifiedRHEL 23 | } 24 | -------------------------------------------------------------------------------- /api/v1/note_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Note"; DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[OSCVEsUnavailable-0] 12 | _ = x[OSCVEsStale-1] 13 | _ = x[LanguageCVEsUnavailable-2] 14 | _ = x[CertifiedRHELScanUnavailable-3] 15 | _ = x[SentinelNote-4] 16 | } 17 | 18 | const _Note_name = "OSCVEsUnavailableOSCVEsStaleLanguageCVEsUnavailableCertifiedRHELScanUnavailableSentinelNote" 19 | 20 | var _Note_index = [...]uint8{0, 17, 28, 51, 79, 91} 21 | 22 | func (i Note) String() string { 23 | if i < 0 || i >= Note(len(_Note_index)-1) { 24 | return "Note(" + strconv.FormatInt(int64(i), 10) + ")" 25 | } 26 | return _Note_name[_Note_index[i]:_Note_index[i+1]] 27 | } 28 | -------------------------------------------------------------------------------- /api/v1/ping/service.go: -------------------------------------------------------------------------------- 1 | package ping 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" 7 | apiGRPC "github.com/stackrox/scanner/api/grpc" 8 | v1 "github.com/stackrox/scanner/generated/scanner/api/v1" 9 | "github.com/stackrox/scanner/pkg/version" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | // Service defines the Ping service. 14 | type Service interface { 15 | apiGRPC.APIService 16 | 17 | v1.PingServiceServer 18 | } 19 | 20 | // NewService returns a new Ping service. 21 | func NewService() Service { 22 | return &serviceImpl{ 23 | version: version.Version, 24 | } 25 | } 26 | 27 | type serviceImpl struct { 28 | v1.UnimplementedPingServiceServer 29 | 30 | version string 31 | } 32 | 33 | func (s *serviceImpl) Ping(context.Context, *v1.Empty) (*v1.PongMessage, error) { 34 | return &v1.PongMessage{ 35 | ScannerVersion: s.version, 36 | Status: "OK", 37 | }, nil 38 | } 39 | 40 | // RegisterServiceServer registers this service with the given gRPC Server. 41 | func (s *serviceImpl) RegisterServiceServer(grpcServer *grpc.Server) { 42 | v1.RegisterPingServiceServer(grpcServer, s) 43 | } 44 | 45 | // RegisterServiceHandler registers this service with the given gRPC Gateway endpoint. 46 | func (s *serviceImpl) RegisterServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { 47 | return v1.RegisterPingServiceHandler(ctx, mux, conn) 48 | } 49 | -------------------------------------------------------------------------------- /benchmarks/analyzeNode/analyze_node_test.go: -------------------------------------------------------------------------------- 1 | package detectconent 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "runtime" 8 | "testing" 9 | 10 | node "github.com/stackrox/scanner/pkg/analyzer/nodes" 11 | // Register the Docker image extractor 12 | _ "github.com/stackrox/scanner/ext/imagefmt/docker" 13 | ) 14 | 15 | func BenchmarkAnalyzeNode(b *testing.B) { 16 | var path = os.Getenv("RHCOS_TEST_PATH") 17 | if len(path) < 1 { 18 | b.Fatal("Invalid file path") 19 | } 20 | fmt.Printf("Current path to file system is: %s", path) 21 | fmt.Println() 22 | runBenchmarkAnalyzeNode(b, path) 23 | } 24 | 25 | func runBenchmarkAnalyzeNode(b *testing.B, pathName string) { 26 | runtime.GC() 27 | 28 | for i := 0; i < b.N; i++ { 29 | node.Analyze(context.Background(), "testNode", pathName, node.AnalyzeOpts{UncertifiedRHEL: false, IsRHCOSRequired: false}) 30 | } 31 | // Memory measuring command: go test -bench=foo -benchmem 32 | 33 | } 34 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: StackRox Scanner 3 | version: 0.0.1 4 | -------------------------------------------------------------------------------- /chart/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: scanner-db-password 5 | namespace: {{ .Values.namespace }} 6 | type: Opaque 7 | data: 8 | password: c3RhY2tyb3hpc2Nvb2wK 9 | -------------------------------------------------------------------------------- /chart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.appName }}-db 5 | namespace: {{ .Values.namespace }} 6 | spec: 7 | ports: 8 | - name: tcp-db 9 | port: 5432 10 | targetPort: postgresql 11 | selector: 12 | app: {{ .Values.appName }}-db 13 | type: ClusterIP 14 | -------------------------------------------------------------------------------- /chart/values.yaml: -------------------------------------------------------------------------------- 1 | appName: scanner 2 | namespace: stackrox 3 | scannerImage: quay.io/stackrox-io/scanner 4 | scannerDBImage: quay.io/stackrox-io/scanner-db 5 | tag: latest 6 | imagePullPolicy: IfNotPresent 7 | 8 | logLevel: info 9 | updateInterval: 1h 10 | -------------------------------------------------------------------------------- /cmd/clair/config_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stackrox/scanner/api" 10 | "github.com/stackrox/scanner/pkg/analyzer" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestLoadConfig(t *testing.T) { 15 | _, filename, _, _ := runtime.Caller(0) 16 | cfg, err := LoadConfig(filepath.Dir(filename) + "/testdata/config.yaml") 17 | assert.NoError(t, err) 18 | 19 | assert.Equal(t, &api.Config{ 20 | HTTPSPort: 8082, 21 | GRPCPort: 8081, 22 | MetricsPort: nil, 23 | }, cfg.API) 24 | 25 | assert.Equal(t, 5*time.Minute, cfg.Updater.Interval) 26 | assert.Equal(t, int64(analyzer.DefaultMaxExtractableFileSizeMB), cfg.MaxExtractableFileSizeMB) 27 | assert.Equal(t, int64(400), cfg.MaxELFExecutableFileSizeMB) 28 | assert.Equal(t, int64(150), cfg.MaxImageFileReaderBufferSizeMB) 29 | assert.Equal(t, "https://central.stackrox.svc", cfg.CentralEndpoint) 30 | assert.Equal(t, "https://sensor.stackrox.svc", cfg.SensorEndpoint) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/updater/common/dump_utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "archive/zip" 5 | "path/filepath" 6 | 7 | "github.com/pkg/errors" 8 | "github.com/stackrox/scanner/pkg/vulndump" 9 | ) 10 | 11 | // OpenGenesisDumpAndExtractManifest opens a zip reader for the given vuln dump, validates it, 12 | // and extracts the manifest from it. Returns an error if the dump is invalid. 13 | // The caller is responsible for closing the returned zip reader. 14 | func OpenGenesisDumpAndExtractManifest(zipPath string) (*zip.ReadCloser, *vulndump.Manifest, error) { 15 | if filepath.Ext(zipPath) != ".zip" { 16 | return nil, nil, errors.Errorf("invalid dump %q; expected zip file", zipPath) 17 | } 18 | zipR, err := zip.OpenReader(zipPath) 19 | if err != nil { 20 | return nil, nil, errors.Wrap(err, "opening zip") 21 | } 22 | manifest, err := vulndump.LoadManifestFromDump(&zipR.Reader) 23 | if err != nil { 24 | return nil, nil, err 25 | } 26 | if !manifest.Since.IsZero() { 27 | return nil, nil, errors.New("invalid dump: not a genesis dump") 28 | } 29 | return zipR, manifest, nil 30 | } 31 | -------------------------------------------------------------------------------- /cmd/updater/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/stackrox/scanner/cmd/updater/diffdumps" 8 | "github.com/stackrox/scanner/cmd/updater/generatedump" 9 | "github.com/stackrox/scanner/cmd/updater/loaddump" 10 | "github.com/stackrox/scanner/cmd/updater/printstats" 11 | 12 | // Registrations. 13 | _ "github.com/stackrox/scanner/database/pgsql" 14 | _ "github.com/stackrox/scanner/ext/vulnsrc/all" 15 | ) 16 | 17 | func main() { 18 | c := &cobra.Command{ 19 | Short: "Commands related to fetching updated vulnerability definitions", 20 | SilenceUsage: true, 21 | } 22 | 23 | c.AddCommand( 24 | diffdumps.Command(), 25 | generatedump.Command(), 26 | loaddump.Command(), 27 | printstats.Command(), 28 | ) 29 | 30 | if err := c.Execute(); err != nil { 31 | // No need to log the error, Cobra does it already. 32 | os.Exit(1) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cmd/updater/printstats/cmd.go: -------------------------------------------------------------------------------- 1 | package printstats 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/pkg/errors" 8 | "github.com/spf13/cobra" 9 | "github.com/stackrox/rox/pkg/utils" 10 | "github.com/stackrox/scanner/cmd/updater/common" 11 | "github.com/stackrox/scanner/pkg/vulndump" 12 | ) 13 | 14 | // Command defines the `print-stats` command. 15 | func Command() *cobra.Command { 16 | c := &cobra.Command{ 17 | Use: "print-stats", 18 | Args: cobra.ExactArgs(1), 19 | RunE: func(_ *cobra.Command, args []string) error { 20 | dumpFile := args[0] 21 | zipR, _, err := common.OpenGenesisDumpAndExtractManifest(dumpFile) 22 | if err != nil { 23 | return errors.Wrap(err, "loading dump") 24 | } 25 | defer utils.IgnoreError(zipR.Close) 26 | vulns, err := vulndump.LoadOSVulnsFromDump(&zipR.Reader) 27 | if err != nil { 28 | return errors.Wrap(err, "loading os vulns from dump") 29 | } 30 | vulnsByNS := make(map[string]int) 31 | for _, vuln := range vulns { 32 | vulnsByNS[vuln.Namespace.Name]++ 33 | } 34 | fmt.Print("Vuln counts by namespace:\n") 35 | keys := make([]string, 0, len(vulnsByNS)) 36 | for key := range vulnsByNS { 37 | keys = append(keys, key) 38 | } 39 | sort.Strings(keys) 40 | for _, k := range keys { 41 | fmt.Printf("%s\t%d\n", k, vulnsByNS[k]) 42 | } 43 | fmt.Printf("Total vulns: %d", len(vulns)) 44 | return nil 45 | }, 46 | } 47 | 48 | return c 49 | } 50 | -------------------------------------------------------------------------------- /cpe/attributes/common/common_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stackrox/scanner/pkg/component" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGenerateNameKeys(t *testing.T) { 11 | cases := []struct { 12 | name string 13 | keys []string 14 | }{ 15 | { 16 | name: "", 17 | }, 18 | { 19 | name: "name", 20 | keys: []string{ 21 | "name", 22 | }, 23 | }, 24 | { 25 | name: "struts-showcase", 26 | keys: []string{ 27 | "struts-showcase", 28 | "struts_showcase", 29 | }, 30 | }, 31 | } 32 | for _, c := range cases { 33 | t.Run(c.name, func(t *testing.T) { 34 | comp := &component.Component{Name: c.name} 35 | assert.ElementsMatch(t, c.keys, GenerateNameKeys(comp).AsSlice()) 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cpe/attributes/dotnetcoreruntime/dotnetcoreruntime.go: -------------------------------------------------------------------------------- 1 | package dotnetcoreruntime 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/facebookincubator/nvdtools/wfn" 7 | "github.com/stackrox/rox/pkg/set" 8 | "github.com/stackrox/scanner/cpe/attributes/common" 9 | "github.com/stackrox/scanner/pkg/component" 10 | ) 11 | 12 | // GetDotNetCoreRuntimeAttributes returns the [ASP].NET attributes for the given component. 13 | func GetDotNetCoreRuntimeAttributes(c *component.Component) []*wfn.Attributes { 14 | vendorSet := set.NewStringSet("microsoft") 15 | 16 | nameSet := set.NewStringSet(c.Name, escapePeriod(c.Name)) 17 | versionSet := set.NewStringSet(c.Version, escapePeriod(c.Version)) 18 | return common.GenerateAttributesFromSets(vendorSet, nameSet, versionSet, "") 19 | } 20 | 21 | func escapePeriod(str string) string { 22 | return strings.ReplaceAll(str, ".", `\.`) 23 | } 24 | -------------------------------------------------------------------------------- /cpe/attributes/node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/facebookincubator/nvdtools/wfn" 5 | "github.com/stackrox/rox/pkg/set" 6 | "github.com/stackrox/scanner/cpe/attributes/common" 7 | "github.com/stackrox/scanner/pkg/component" 8 | ) 9 | 10 | // GetNodeAttributes returns the attributes from the given NPM component. 11 | func GetNodeAttributes(c *component.Component) []*wfn.Attributes { 12 | vendorSet := set.NewStringSet() 13 | nameSet := common.GenerateNameKeys(c) 14 | versionSet := common.GenerateVersionKeys(c) 15 | 16 | return common.GenerateAttributesFromSets(vendorSet, nameSet, versionSet, `node\.js`) 17 | } 18 | -------------------------------------------------------------------------------- /cpe/attributes/python/python_test.go: -------------------------------------------------------------------------------- 1 | package python 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParseAuthorEmailAsVendor(t *testing.T) { 10 | cases := []struct { 11 | email string 12 | vendor string 13 | }{ 14 | { 15 | email: "", 16 | vendor: "", 17 | }, 18 | { 19 | email: "a", 20 | vendor: "", 21 | }, 22 | { 23 | email: "a@", 24 | vendor: "", 25 | }, 26 | { 27 | email: "a@stackrox", 28 | vendor: "stackrox", 29 | }, 30 | { 31 | email: "a@stackrox.com", 32 | vendor: "stackrox", 33 | }, 34 | } 35 | for _, c := range cases { 36 | t.Run(c.email, func(t *testing.T) { 37 | assert.Equal(t, c.vendor, parseAuthorEmailAsVendor(c.email)) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cpe/attributes/ruby/ruby.go: -------------------------------------------------------------------------------- 1 | package ruby 2 | 3 | import ( 4 | "github.com/facebookincubator/nvdtools/wfn" 5 | "github.com/stackrox/rox/pkg/set" 6 | "github.com/stackrox/scanner/cpe/attributes/common" 7 | "github.com/stackrox/scanner/pkg/component" 8 | ) 9 | 10 | // GetRubyAttributes gets the Ruby-related attributes from the given component. 11 | func GetRubyAttributes(c *component.Component) []*wfn.Attributes { 12 | vendorSet := set.NewStringSet() 13 | nameSet := common.GenerateNameKeys(c) 14 | versionSet := common.GenerateVersionKeys(c) 15 | return common.GenerateAttributesFromSets(vendorSet, nameSet, versionSet, "") 16 | } 17 | -------------------------------------------------------------------------------- /cpe/match/match.go: -------------------------------------------------------------------------------- 1 | package match 2 | 3 | import ( 4 | "github.com/facebookincubator/nvdtools/cvefeed" 5 | "github.com/facebookincubator/nvdtools/wfn" 6 | "github.com/stackrox/scanner/database" 7 | "github.com/stackrox/scanner/pkg/component" 8 | ) 9 | 10 | // Result holds the results from performing vulnerability matching with NVD data. 11 | type Result struct { 12 | CVE cvefeed.Vuln 13 | CPE wfn.AttributesWithFixedIn 14 | VersionOverride string 15 | Component *component.Component 16 | Vuln *database.Vulnerability 17 | } 18 | -------------------------------------------------------------------------------- /cpe/nvdtoolscache/cache.go: -------------------------------------------------------------------------------- 1 | package nvdtoolscache 2 | 3 | import ( 4 | "github.com/facebookincubator/nvdtools/cvefeed" 5 | "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" 6 | "github.com/stackrox/scanner/pkg/cache" 7 | ) 8 | 9 | // Cache defines an NVD vulnerability cache. 10 | type Cache interface { 11 | GetVulnsForProducts(products []string) ([]cvefeed.Vuln, error) 12 | GetVulnsForComponent(vendor, product, version string) ([]*NVDCVEItemWithFixedIn, error) 13 | 14 | cache.Cache 15 | } 16 | 17 | // NVDCVEItemWithFixedIn is a CVE from NVD. 18 | type NVDCVEItemWithFixedIn struct { 19 | *schema.NVDCVEFeedJSON10DefCVEItem 20 | FixedIn string 21 | } 22 | -------------------------------------------------------------------------------- /cpe/nvdtoolscache/load_test.go: -------------------------------------------------------------------------------- 1 | package nvdtoolscache 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestCPEIsApplicationOrLinuxKernel(t *testing.T) { 10 | assert.True(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:a:netapp:snapmanager:-:*:*:*:*:oracle:*:*")) 11 | assert.True(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:o:linux:linux_kernel:5:*:*:*:*:*:*:*")) 12 | assert.False(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:o:fedoraproject:fedora:35:*:*:*:*:*:*:*")) 13 | assert.False(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:a:oracle:*:14.3.0:*:*:*:*:*:*:*")) 14 | assert.False(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:a:oracle::14.3.0:*:*:*:*:*:*:*")) 15 | assert.False(t, cpeIsApplicationOrLinuxKernel("cpe:2.3:invalid")) 16 | } 17 | -------------------------------------------------------------------------------- /cpe/nvdtoolscache/singleton.go: -------------------------------------------------------------------------------- 1 | package nvdtoolscache 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/stackrox/rox/pkg/utils" 9 | ) 10 | 11 | var ( 12 | once sync.Once 13 | instance Cache 14 | ) 15 | 16 | // Singleton returns the cache instance to use. 17 | func Singleton() Cache { 18 | once.Do(func() { 19 | var err error 20 | instance, err = New() 21 | utils.Must(err) 22 | 23 | if definitionsDir := os.Getenv("NVD_DEFINITIONS_DIR"); definitionsDir != "" { 24 | log.Info("Loading NVD definitions into cache") 25 | utils.Must(instance.LoadFromDirectory(definitionsDir)) 26 | log.Info("Done loading NVD definitions into cache") 27 | } 28 | }) 29 | return instance 30 | } 31 | -------------------------------------------------------------------------------- /cpe/validation/all/all.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | // Import the source validators 5 | _ "github.com/stackrox/scanner/cpe/validation/dotnetcoreruntime" 6 | _ "github.com/stackrox/scanner/cpe/validation/java" 7 | _ "github.com/stackrox/scanner/cpe/validation/node" 8 | _ "github.com/stackrox/scanner/cpe/validation/python" 9 | _ "github.com/stackrox/scanner/cpe/validation/ruby" 10 | ) 11 | -------------------------------------------------------------------------------- /cpe/validation/dotnetcoreruntime/dotnetcoreruntime.go: -------------------------------------------------------------------------------- 1 | package dotnetcoreruntime 2 | 3 | import ( 4 | "github.com/stackrox/scanner/cpe/match" 5 | "github.com/stackrox/scanner/cpe/validation" 6 | "github.com/stackrox/scanner/pkg/component" 7 | ) 8 | 9 | func init() { 10 | validation.Register(component.DotNetCoreRuntimeSourceType, &validator{}) 11 | } 12 | 13 | type validator struct{} 14 | 15 | func (v validator) ValidateResult(_ match.Result) bool { 16 | return true 17 | } 18 | -------------------------------------------------------------------------------- /cpe/validation/java/java.go: -------------------------------------------------------------------------------- 1 | package java 2 | 3 | import ( 4 | "github.com/stackrox/scanner/cpe/match" 5 | "github.com/stackrox/scanner/cpe/validation" 6 | "github.com/stackrox/scanner/pkg/component" 7 | ) 8 | 9 | type validator struct{} 10 | 11 | func (v validator) ValidateResult(result match.Result) bool { 12 | return validation.TargetSWMatches(result, "java") 13 | } 14 | 15 | func init() { 16 | validation.Register(component.JavaSourceType, &validator{}) 17 | } 18 | -------------------------------------------------------------------------------- /cpe/validation/node/node.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "strings" 5 | 6 | log "github.com/sirupsen/logrus" 7 | "github.com/stackrox/rox/pkg/set" 8 | "github.com/stackrox/scanner/cpe/match" 9 | "github.com/stackrox/scanner/cpe/validation" 10 | "github.com/stackrox/scanner/pkg/component" 11 | ) 12 | 13 | var ( 14 | // brace_expansion 1.1.6 -> CVE-2017-18077 15 | // qs 6.2.1 -> CVE-2017-1000048 16 | // cryptiles 1.1.6 -> CVE-2018-1000620 17 | knownPkgs = set.NewFrozenStringSet("cryptiles", "qs", "brace_expansion", "jquery", "dompurify") 18 | 19 | keywords = []string{".js", "node", "npm"} 20 | ) 21 | 22 | type validator struct{} 23 | 24 | func (v validator) ValidateResult(result match.Result) bool { 25 | if knownPkgs.Contains(result.CPE.Product) { 26 | return true 27 | } 28 | for _, a := range result.CVE.Config() { 29 | if a.TargetSW == `node\.js` { 30 | return true 31 | } 32 | } 33 | descLower := strings.ToLower(result.Vuln.Description) 34 | for _, k := range keywords { 35 | if strings.Contains(descLower, k) { 36 | return true 37 | } 38 | } 39 | log.Debugf("NodeJS failed validation: %s %s", result.CPE.Product, result.CVE.ID()) 40 | return false 41 | } 42 | 43 | func init() { 44 | validation.Register(component.NPMSourceType, &validator{}) 45 | } 46 | -------------------------------------------------------------------------------- /cpe/validation/python/python.go: -------------------------------------------------------------------------------- 1 | package python 2 | 3 | import ( 4 | "strings" 5 | 6 | log "github.com/sirupsen/logrus" 7 | "github.com/stackrox/rox/pkg/set" 8 | "github.com/stackrox/scanner/cpe/match" 9 | "github.com/stackrox/scanner/cpe/validation" 10 | "github.com/stackrox/scanner/pkg/component" 11 | "github.com/stackrox/scanner/pkg/stringhelpers" 12 | ) 13 | 14 | var ( 15 | knownPkgs = set.NewFrozenStringSet("ansible", "jinja2", "supervisor", "html5lib", "sqlalchemy", "paramiko") 16 | 17 | keywords = []string{ 18 | "beaker", "flask", "pallets", "py", "pickle", 19 | } 20 | ) 21 | 22 | type validator struct{} 23 | 24 | func (v validator) ValidateResult(result match.Result) bool { 25 | if !validation.TargetSWMatches(result, "python") { 26 | return false 27 | } 28 | if result.CPE.Vendor != "" { 29 | return true 30 | } 31 | if knownPkgs.Contains(result.CPE.Product) { 32 | return true 33 | } 34 | if stringhelpers.AnyContain([]string{result.CPE.Vendor, result.CPE.Product}, "py") { 35 | return true 36 | } 37 | desc := strings.ToLower(result.Vuln.Description) 38 | for _, k := range keywords { 39 | if strings.Contains(desc, k) { 40 | return true 41 | } 42 | } 43 | log.Debugf("Python (%s %s): %s failed validation: %v", result.CPE.Vendor, result.CPE.Product, result.CVE.ID(), result.Vuln.Description) 44 | return false 45 | } 46 | 47 | func init() { 48 | validation.Register(component.PythonSourceType, &validator{}) 49 | } 50 | -------------------------------------------------------------------------------- /cpe/validation/validation.go: -------------------------------------------------------------------------------- 1 | package validation 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/facebookincubator/nvdtools/wfn" 7 | "github.com/stackrox/scanner/cpe/match" 8 | "github.com/stackrox/scanner/pkg/component" 9 | ) 10 | 11 | var ( 12 | // Validators holds each registered Validator. 13 | Validators = make(map[component.SourceType]Validator) 14 | ) 15 | 16 | // Validator is the common interface for validating NVD results. 17 | type Validator interface { 18 | ValidateResult(result match.Result) bool 19 | } 20 | 21 | // Register registers the given validator. 22 | func Register(src component.SourceType, validator Validator) { 23 | if _, ok := Validators[src]; ok { 24 | panic(fmt.Sprintf("%q has already been registered", src)) 25 | } 26 | Validators[src] = validator 27 | } 28 | 29 | // TargetSWMatches checks if the TargetSW from the resulting CPE matches the language 30 | // or matches any language 31 | func TargetSWMatches(res match.Result, lang string) bool { 32 | for _, a := range res.CVE.Config() { 33 | if a.TargetSW == wfn.Any || a.TargetSW == lang { 34 | return true 35 | } 36 | } 37 | return false 38 | } 39 | -------------------------------------------------------------------------------- /database/options.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | // DatastoreOptions defines the options for reading from and writing to the database. 4 | type DatastoreOptions struct { 5 | // WithFeatures is a read-only option. 6 | // `true` indicates the Features field should be filled. 7 | WithFeatures bool 8 | // WithVulnerabilities is a read-only option. 9 | // `true` means the Features field should be filled 10 | // and their AffectedBy fields should contain every vulnerability that 11 | // affect them. 12 | WithVulnerabilities bool 13 | // UncertifiedRHEL indicates the returned results should be for an 14 | // uncertified RHEL layer, if the layer's namespace is RHEL. 15 | UncertifiedRHEL bool 16 | } 17 | 18 | // GetWithFeatures returns "true" if WithFeatures is "true"; "false" otherwise. 19 | func (o *DatastoreOptions) GetWithFeatures() bool { 20 | if o == nil { 21 | return false 22 | } 23 | return o.WithFeatures 24 | } 25 | 26 | // GetWithVulnerabilities returns "true" if WithVulnerabilities is "true"; "false" otherwise. 27 | func (o *DatastoreOptions) GetWithVulnerabilities() bool { 28 | if o == nil { 29 | return false 30 | } 31 | return o.WithVulnerabilities 32 | } 33 | 34 | // GetUncertifiedRHEL returns "true" if UncertifiedRHEL is "true"; "false" otherwise. 35 | func (o *DatastoreOptions) GetUncertifiedRHEL() bool { 36 | if o == nil { 37 | return false 38 | } 39 | return o.UncertifiedRHEL 40 | } 41 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00003_add_indexes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package migrations 16 | 17 | import "github.com/remind101/migrate" 18 | 19 | func init() { 20 | RegisterMigration(migrate.Migration{ 21 | ID: 3, 22 | Up: migrate.Queries([]string{ 23 | `CREATE UNIQUE INDEX namespace_name_key ON Namespace (name);`, 24 | `CREATE INDEX vulnerability_name_idx ON Vulnerability (name);`, 25 | `CREATE INDEX vulnerability_namespace_id_name_idx ON Vulnerability (namespace_id, name);`, 26 | `CREATE UNIQUE INDEX featureversion_feature_id_version_key ON FeatureVersion (feature_id, version);`, 27 | }), 28 | Down: migrate.Queries([]string{ 29 | `DROP INDEX namespace_name_key;`, 30 | `DROP INDEX vulnerability_name_idx;`, 31 | `DROP INDEX vulnerability_namespace_id_name_idx;`, 32 | `DROP INDEX featureversion_feature_id_version_key;`, 33 | }), 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00004_add_index_notification_deleted_at.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package migrations 16 | 17 | import "github.com/remind101/migrate" 18 | 19 | func init() { 20 | RegisterMigration(migrate.Migration{ 21 | ID: 4, 22 | Up: migrate.Queries([]string{ 23 | `CREATE INDEX vulnerability_notification_deleted_at_idx ON Vulnerability_Notification (deleted_at);`, 24 | }), 25 | Down: migrate.Queries([]string{ 26 | `DROP INDEX vulnerability_notification_deleted_at_idx;`, 27 | }), 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00005_ldfv_index.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package migrations 16 | 17 | import "github.com/remind101/migrate" 18 | 19 | func init() { 20 | RegisterMigration(migrate.Migration{ 21 | ID: 5, 22 | Up: migrate.Queries([]string{ 23 | `CREATE INDEX layer_diff_featureversion_layer_id_modification_idx ON Layer_diff_FeatureVersion (layer_id, modification);`, 24 | }), 25 | Down: migrate.Queries([]string{ 26 | `DROP INDEX layer_diff_featureversion_layer_id_modification_idx;`, 27 | }), 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00006_add_version_format.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package migrations 16 | 17 | import "github.com/remind101/migrate" 18 | 19 | func init() { 20 | RegisterMigration(migrate.Migration{ 21 | ID: 6, 22 | Up: migrate.Queries([]string{ 23 | `ALTER TABLE Namespace ADD COLUMN version_format varchar(128);`, 24 | `UPDATE Namespace SET version_format = 'rpm' WHERE name LIKE 'rhel%' OR name LIKE 'centos%' OR name LIKE 'fedora%' OR name LIKE 'amzn%' OR name LIKE 'scientific%' OR name LIKE 'ol%' OR name LIKE 'oracle%';`, 25 | `UPDATE Namespace SET version_format = 'dpkg' WHERE version_format is NULL;`, 26 | }), 27 | Down: migrate.Queries([]string{ 28 | `ALTER TABLE Namespace DROP COLUMN version_format;`, 29 | }), 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00007_add_image.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 7, 8 | Up: migrate.Queries([]string{ 9 | `CREATE TABLE IF NOT EXISTS ImageToLayer( 10 | layer varchar, 11 | name varchar, 12 | sha varchar, 13 | PRIMARY KEY( sha, name ));`, 14 | 15 | `CREATE TABLE IF NOT EXISTS LanguageLayer ( 16 | layer_id int NOT NULL, 17 | layer_name VARCHAR(128) NOT NULL UNIQUE, 18 | component_data BYTEA NOT NULL);`, 19 | }), 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00008_add_removed_components.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 8, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE LanguageLayer 10 | ADD COLUMN removed_components text[];`, 11 | }), 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00009_distroless.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 9, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE Layer 10 | ADD COLUMN distroless bool DEFAULT false; `, 11 | }), 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00012_add_uncertified_rhel.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 12, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE ImageToLayer ADD COLUMN uncertified_rhel boolean`, 10 | `UPDATE ImageToLayer SET uncertified_rhel = false`, 11 | `ALTER TABLE ImageToLayer DROP CONSTRAINT ImageToLayer_pkey`, 12 | `ALTER TABLE ImageToLayer ADD PRIMARY KEY (sha, name, uncertified_rhel)`, 13 | }), 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00013_add_vuln_title_rhel.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 13, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE vuln ADD COLUMN title TEXT NOT NULL DEFAULT ''`, 10 | }), 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00014_add_layer_parent_hash.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 14, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE ImageToLayer ADD COLUMN lineage varchar`, 10 | `ALTER TABLE LanguageLayer ADD COLUMN lineage varchar`, 11 | `ALTER TABLE Layer ADD COLUMN lineage varchar`, 12 | `ALTER TABLE Layer_diff_FeatureVersion ADD COLUMN lineage varchar`, 13 | `ALTER TABLE Layer DROP CONSTRAINT layer_name_key`, 14 | `ALTER TABLE Layer ADD UNIQUE (name, lineage)`, 15 | }), 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00015_add_executables.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 15, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE rhelv2_package ADD COLUMN executables TEXT[]`, 10 | `ALTER TABLE FeatureVersion ADD COLUMN executables TEXT[]`, 11 | }), 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00016_add_shared_objects.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 16, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE FeatureVersion ADD COLUMN IF NOT EXISTS executable_to_dependencies BYTEA`, 10 | `ALTER TABLE FeatureVersion ADD COLUMN IF NOT EXISTS library_to_dependencies BYTEA`, 11 | `ALTER TABLE FeatureVersion DROP COLUMN IF EXISTS executables`, 12 | }), 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00017_rhel_add_shared_objects.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 17, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE rhelv2_package ADD COLUMN IF NOT EXISTS executable_to_dependencies BYTEA`, 10 | `ALTER TABLE rhelv2_package ADD COLUMN IF NOT EXISTS library_to_dependencies BYTEA`, 11 | `ALTER TABLE rhelv2_package DROP COLUMN IF EXISTS executables`, 12 | }), 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00019_add_vuln_state_rhel.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 19, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE vuln_v2 ADD COLUMN IF NOT EXISTS resolution_state TEXT`, 10 | }), 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00020_languagelayer_lineage_pk.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 20, 8 | Up: migrate.Queries([]string{ 9 | `ALTER TABLE LanguageLayer DROP CONSTRAINT IF EXISTS languagelayer_pkey`, 10 | `ALTER TABLE LanguageLayer DROP CONSTRAINT IF EXISTS languagelayer_layer_name_key`, 11 | `ALTER TABLE LanguageLayer ADD PRIMARY KEY (layer_name, lineage)`, 12 | }), 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /database/pgsql/migrations/00021_rhel_layer_lineage.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import "github.com/remind101/migrate" 4 | 5 | func init() { 6 | RegisterMigration(migrate.Migration{ 7 | ID: 21, 8 | Up: migrate.Queries([]string{ 9 | 10 | // The lineage column mimics the existing `layer` table. The parent_lineage column is used 11 | // instead of equivalent parent_id column from the 'layer' table to avoid an extra query 12 | // on insert (which would be necessary to determine the parent id). 13 | `ALTER TABLE rhelv2_layer ADD COLUMN IF NOT EXISTS lineage varchar;`, 14 | `ALTER TABLE rhelv2_layer ADD COLUMN IF NOT EXISTS parent_lineage varchar`, 15 | 16 | // Create a new unique constraint that includes lineage (and drop the old constraint) 17 | `ALTER TABLE rhelv2_layer ADD CONSTRAINT rhelv2_layer_hash_lineage_key UNIQUE (hash, lineage)`, 18 | `ALTER TABLE rhelv2_layer DROP CONSTRAINT IF EXISTS rhelv2_layer_hash_key`, 19 | 20 | // Create additional index to improve performance when recursively traversing parents. 21 | `CREATE INDEX IF NOT EXISTS rhelv2_layer_parent_idx on rhelv2_layer (parent_hash, parent_lineage)`, 22 | }), 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /database/pgsql/migrations/migrations.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package migrations regroups every migrations available to the pgsql database 16 | // backend. 17 | package migrations 18 | 19 | import "github.com/remind101/migrate" 20 | 21 | // Migrations contains every available migrations. 22 | var Migrations []migrate.Migration 23 | 24 | // RegisterMigration adds the specified migration to the available migrations. 25 | func RegisterMigration(migration migrate.Migration) { 26 | Migrations = append(Migrations, migration) 27 | } 28 | -------------------------------------------------------------------------------- /database/severity_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package database 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestCompareSeverity(t *testing.T) { 24 | assert.Equal(t, MediumSeverity.Compare(MediumSeverity), 0, "Severity comparison failed") 25 | assert.True(t, MediumSeverity.Compare(HighSeverity) < 0, "Severity comparison failed") 26 | assert.True(t, CriticalSeverity.Compare(LowSeverity) > 0, "Severity comparison failed") 27 | } 28 | 29 | func TestParseSeverity(t *testing.T) { 30 | _, err := NewSeverity("Test") 31 | assert.Equal(t, ErrFailedToParseSeverity, err) 32 | 33 | _, err = NewSeverity("Unknown") 34 | assert.Nil(t, err) 35 | } 36 | -------------------------------------------------------------------------------- /e2etests/empty.go: -------------------------------------------------------------------------------- 1 | package e2etests 2 | 3 | // This file exists to keep the compiler happy. 4 | -------------------------------------------------------------------------------- /e2etests/layer_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | // +build e2e 3 | 4 | package e2etests 5 | 6 | import ( 7 | "context" 8 | "testing" 9 | 10 | v1 "github.com/stackrox/scanner/generated/scanner/api/v1" 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestLayersAndLocations(t *testing.T) { 16 | conn := connectToScanner(t) 17 | client := v1.NewImageScanServiceClient(conn) 18 | scanResp := scanQuayStackRoxImage(client, "quay.io/rhacs-eng/qa:syndesis-s2i-9c3ea4777a61896364445fb13000e84da0d4596f478ff0520d3140a69758b6f2", false, t) 19 | scan, err := client.GetImageScan(context.Background(), &v1.GetImageScanRequest{ 20 | ImageSpec: scanResp.GetImage(), 21 | }) 22 | require.NoError(t, err) 23 | 24 | // Make sure that all java packages have a location 25 | for _, f := range scan.GetImage().GetFeatures() { 26 | if f.FeatureType != "rpm" { 27 | assert.NotEmpty(t, f.GetLocation()) 28 | } 29 | } 30 | // sha256:2a28f35f97b8a65f89f83a39efa0b3926a2f5e442c71ae99c549af29d535e54c is a layer that chowns the files 31 | for _, f := range scan.GetImage().GetFeatures() { 32 | assert.NotEqual(t, "sha256:2a28f35f97b8a65f89f83a39efa0b3926a2f5e442c71ae99c549af29d535e54c", f.GetAddedByLayer()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /e2etests/utils_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e || slim_e2e 2 | // +build e2e slim_e2e 3 | 4 | package e2etests 5 | 6 | import ( 7 | "testing" 8 | 9 | v1 "github.com/stackrox/scanner/api/v1" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func getMatchingFeature(t *testing.T, featureList []v1.Feature, featureToFind v1.Feature, allowNotFound bool) *v1.Feature { 14 | candidateIdx := -1 15 | for i, f := range featureList { 16 | if f.Name == featureToFind.Name && f.Version == featureToFind.Version { 17 | require.Equal(t, -1, candidateIdx, "Found multiple features for %s/%s", f.Name, f.Version) 18 | candidateIdx = i 19 | } 20 | } 21 | if allowNotFound && candidateIdx == -1 { 22 | return nil 23 | } 24 | require.NotEqual(t, -1, candidateIdx, "Feature %+v not in list: %+v", featureToFind, featureList) 25 | return &featureList[candidateIdx] 26 | } 27 | -------------------------------------------------------------------------------- /ext/featurefmt/apk/testdata/README.txt: -------------------------------------------------------------------------------- 1 | The files here are either copied from Alpine-based images or are created to mimic the exact format 2 | of similar files found in Alpine-based images. This includes the number of new lines at the end of the files. 3 | 4 | Please be careful when updating. 5 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/README.txt: -------------------------------------------------------------------------------- 1 | The files here are either copied from Debian-based images or are created to mimic the exact format 2 | of similar files found in Debian-based images. This includes the number of new lines at the end of the files. 3 | 4 | Please be careful when updating. 5 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/go.mod: -------------------------------------------------------------------------------- 1 | // Files with ":" in the name are not valid files to put in a ZIP. 2 | // Because of this, adding the Scanner module to Rox does not work properly. 3 | // To alleviate this, we have an empty module here, so these files are ignored. 4 | // See https://github.com/golang/go/issues/41402 for more information. 5 | module github.com/stackrox/scanner/ignore 6 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/libgcc1:amd64.list: -------------------------------------------------------------------------------- 1 | /. 2 | /not 3 | /not/an 4 | /not/an/executable 5 | /i 6 | /i/am 7 | /i/am/an 8 | /i/am/an/executable 9 | /lib/linux/libgcc5.so.1 10 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/libpam-modules-bin.list: -------------------------------------------------------------------------------- 1 | /. 2 | /another 3 | /another/one 4 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/libpam-runtime.list: -------------------------------------------------------------------------------- 1 | /. 2 | /test 3 | /test/executable 4 | /test/non 5 | /test/non/executable 6 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/pkg-source.list: -------------------------------------------------------------------------------- 1 | /. 2 | /src-do-not-exec-me 3 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/pkg1:amd64.list: -------------------------------------------------------------------------------- 1 | /. 2 | /exec-me 3 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/pkg2.list: -------------------------------------------------------------------------------- 1 | /. 2 | /exec-me-2 3 | /my-jar.jar 4 | /lib/linux/libsomelib.so.1 5 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/statusd-base: -------------------------------------------------------------------------------- 1 | Package: base-files 2 | Version: 10.3+deb10u6 3 | Architecture: amd64 4 | Essential: yes 5 | Maintainer: Santiago Vila 6 | Installed-Size: 340 7 | Pre-Depends: awk 8 | Breaks: debian-security-support (<< 2019.04.25), initscripts (<< 2.88dsf-13.3), sendfile (<< 2.1b.20080616-5.2~) 9 | Replaces: base, dpkg (<= 1.15.0), miscutils 10 | Provides: base 11 | Section: admin 12 | Priority: required 13 | Multi-Arch: foreign 14 | Description: Debian base system miscellaneous files 15 | This package contains the basic filesystem hierarchy of a Debian system, and 16 | several important miscellaneous files, such as /etc/debian_version, 17 | /etc/host.conf, /etc/issue, /etc/motd, /etc/profile, and others, 18 | and the text of several common licenses in use on Debian systems. 19 | -------------------------------------------------------------------------------- /ext/featurefmt/dpkg/testdata/statusd-netbase: -------------------------------------------------------------------------------- 1 | Package: netbase 2 | Version: 5.6 3 | Architecture: all 4 | Maintainer: Marco d'Itri 5 | Installed-Size: 44 6 | Section: admin 7 | Priority: important 8 | Multi-Arch: foreign 9 | Description: Basic TCP/IP networking system 10 | This package provides the necessary infrastructure for basic TCP/IP based 11 | networking. 12 | . 13 | In particular, it supplies tables of common name-to-number mappings in 14 | /etc/services, /etc/rpc, and /etc/protocols. 15 | -------------------------------------------------------------------------------- /ext/featurefmt/rpm/testdata/Packages: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/ext/featurefmt/rpm/testdata/Packages -------------------------------------------------------------------------------- /ext/featurens/options.go: -------------------------------------------------------------------------------- 1 | package featurens 2 | 3 | // DetectorOptions are the options used for base-OS detectors. 4 | type DetectorOptions struct { 5 | UncertifiedRHEL bool 6 | } 7 | 8 | // GetUncertifiedRHEL returns "true" if UncertifiedRHEL is true; "false" otherwise. 9 | func (do *DetectorOptions) GetUncertifiedRHEL() bool { 10 | if do == nil { 11 | return false 12 | } 13 | 14 | return do.UncertifiedRHEL 15 | } 16 | -------------------------------------------------------------------------------- /ext/featurens/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // NormalizeOSName normalizes the given OS name for consistency. 4 | func NormalizeOSName(os string) string { 5 | switch os { 6 | case "ol", "oracle": 7 | return "oracle" 8 | default: 9 | return os 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ext/imagefmt/docker/docker.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package docker implements an imagefmt.Extractor for docker formatted 16 | // container image layers. 17 | package docker 18 | 19 | import ( 20 | "io" 21 | 22 | "github.com/stackrox/scanner/ext/imagefmt" 23 | "github.com/stackrox/scanner/pkg/matcher" 24 | "github.com/stackrox/scanner/pkg/tarutil" 25 | ) 26 | 27 | type format struct{} 28 | 29 | func init() { 30 | imagefmt.RegisterExtractor("docker", &format{}) 31 | } 32 | 33 | func (f format) ExtractFiles(layerReader io.ReadCloser, filenameMatcher matcher.Matcher) (tarutil.LayerFiles, error) { 34 | return tarutil.ExtractFiles(layerReader, filenameMatcher) 35 | } 36 | -------------------------------------------------------------------------------- /ext/kernelparser/all/all.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | // Import all the kernelparsers 5 | _ "github.com/stackrox/scanner/ext/kernelparser/amzn" 6 | _ "github.com/stackrox/scanner/ext/kernelparser/cos" 7 | _ "github.com/stackrox/scanner/ext/kernelparser/debian" 8 | _ "github.com/stackrox/scanner/ext/kernelparser/rhel" 9 | _ "github.com/stackrox/scanner/ext/kernelparser/ubuntu" 10 | _ "github.com/stackrox/scanner/ext/kernelparser/windows" 11 | ) 12 | -------------------------------------------------------------------------------- /ext/kernelparser/amzn/amzn.go: -------------------------------------------------------------------------------- 1 | package amzn 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stackrox/scanner/database" 7 | "github.com/stackrox/scanner/ext/kernelparser" 8 | ) 9 | 10 | const ( 11 | featureName = "kernel" 12 | namespace = "amzn:2" 13 | format = "rpm" 14 | ) 15 | 16 | func init() { 17 | kernelparser.RegisterParser("amzn", parser) 18 | } 19 | 20 | func parser(_ database.Datastore, kernelVersion, _ string) (*kernelparser.ParseMatch, error) { 21 | if !strings.Contains(kernelVersion, "amzn2") { 22 | return nil, kernelparser.ErrKernelUnrecognized 23 | } 24 | return &kernelparser.ParseMatch{ 25 | Namespace: namespace, 26 | Format: format, 27 | FeatureName: featureName, 28 | Version: kernelVersion, 29 | }, nil 30 | } 31 | -------------------------------------------------------------------------------- /ext/kernelparser/cos/cos.go: -------------------------------------------------------------------------------- 1 | package cos 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stackrox/scanner/database" 7 | "github.com/stackrox/scanner/ext/kernelparser" 8 | ) 9 | 10 | func init() { 11 | kernelparser.RegisterParser("cos", parser) 12 | } 13 | 14 | func parser(_ database.Datastore, kernelVersion, osImage string) (*kernelparser.ParseMatch, error) { 15 | if strings.HasSuffix(kernelVersion, "+") && strings.Contains(osImage, "container-optimized") { 16 | // Google COS kernel is unsupported at this time. 17 | return nil, kernelparser.ErrKernelUnsupported 18 | } 19 | return nil, kernelparser.ErrKernelUnrecognized 20 | } 21 | -------------------------------------------------------------------------------- /ext/kernelparser/cos/cos_test.go: -------------------------------------------------------------------------------- 1 | package cos 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/stackrox/scanner/ext/kernelparser" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestParser(t *testing.T) { 13 | cases := []struct { 14 | kernelVersion string 15 | osImage string 16 | err error 17 | }{ 18 | { 19 | kernelVersion: "5.10.101+", 20 | osImage: "Container-Optimized OS from Google", 21 | 22 | err: kernelparser.ErrKernelUnsupported, 23 | }, 24 | { 25 | kernelVersion: "4.18.0-193.14.3.el8_2.x86_64", 26 | osImage: "Red Hat Enterprise Linux CoreOS 45.82.202008101249-0 (Ootpa)", 27 | 28 | err: kernelparser.ErrKernelUnrecognized, 29 | }, 30 | } 31 | 32 | for _, c := range cases { 33 | t.Run(fmt.Sprintf("%s-%s", c.kernelVersion, c.osImage), func(t *testing.T) { 34 | osImage := strings.ToLower(c.osImage) 35 | 36 | match, err := parser(nil, c.kernelVersion, osImage) 37 | assert.Nil(t, match) 38 | assert.Equal(t, c.err, err) 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ext/kernelparser/debian/debian.go: -------------------------------------------------------------------------------- 1 | package debian 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | 8 | "github.com/stackrox/scanner/database" 9 | "github.com/stackrox/scanner/ext/kernelparser" 10 | ) 11 | 12 | const ( 13 | featureName = "linux" 14 | format = "dpkg" 15 | 16 | gardenLinux = "debian:11" 17 | ) 18 | 19 | var ( 20 | regex = regexp.MustCompile(`[0-9]+`) 21 | ) 22 | 23 | func init() { 24 | kernelparser.RegisterParser("debian", parser) 25 | } 26 | 27 | func parser(_ database.Datastore, kernelVersion, osImage string) (*kernelparser.ParseMatch, error) { 28 | if strings.Contains(osImage, "garden") { 29 | return &kernelparser.ParseMatch{ 30 | Namespace: gardenLinux, 31 | Format: format, 32 | FeatureName: featureName, 33 | Version: kernelVersion, 34 | }, nil 35 | } 36 | 37 | if !strings.Contains(osImage, "debian") { 38 | return nil, kernelparser.ErrKernelUnrecognized 39 | } 40 | 41 | matches := regex.FindStringSubmatch(osImage) 42 | if len(matches) == 0 { 43 | return nil, fmt.Errorf("could not find Debian version in OS string: %q", osImage) 44 | } 45 | if len(matches) > 1 { 46 | return nil, fmt.Errorf("found multiple Debian versions in OS string: %q", osImage) 47 | } 48 | 49 | return &kernelparser.ParseMatch{ 50 | Namespace: fmt.Sprintf("debian:%s", matches[0]), 51 | Format: format, 52 | FeatureName: featureName, 53 | Version: kernelVersion, 54 | }, nil 55 | } 56 | -------------------------------------------------------------------------------- /ext/kernelparser/errors.go: -------------------------------------------------------------------------------- 1 | package kernelparser 2 | 3 | import "github.com/pkg/errors" 4 | 5 | var ( 6 | // ErrKernelUnrecognized indicates the kernel is unrecognized by the parser. 7 | // Some other parser may still recognize it, though. 8 | ErrKernelUnrecognized = errors.New("Kernel unrecognized") 9 | 10 | // ErrKernelUnsupported indicates the kernel is unsupported. 11 | // This is typically used for node scanning when the kernel is unsupported 12 | // but the other node components may still be supported. 13 | ErrKernelUnsupported = errors.New("Kernel unsupported") 14 | 15 | // ErrNodeUnsupported indicates the entire node is unsupported. 16 | // This is typically is used for node scanning when the node is running 17 | // an OS which is completely unsupported. 18 | ErrNodeUnsupported = errors.New("Node unsupported") 19 | ) 20 | -------------------------------------------------------------------------------- /ext/kernelparser/register.go: -------------------------------------------------------------------------------- 1 | package kernelparser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/stackrox/scanner/database" 7 | ) 8 | 9 | var ( 10 | // Parsers contains all the registered kernel parsers. 11 | Parsers = make(map[string]Parser) 12 | ) 13 | 14 | // ParseMatch is the return value for the Parser function. 15 | type ParseMatch struct { 16 | Namespace string 17 | Format string 18 | FeatureName string 19 | Version string 20 | } 21 | 22 | // Parser is a kernel parser. 23 | // osImage is expected to be lowercase (for example by calling `strings.ToLower(osImage)`). 24 | // Returns a non-nil *ParseMatch if the kernel is supported. 25 | type Parser func(db database.Datastore, kernelVersion, osImage string) (*ParseMatch, error) 26 | 27 | // RegisterParser registers the given kernel parser. 28 | func RegisterParser(name string, parser Parser) { 29 | if _, ok := Parsers[name]; ok { 30 | panic(fmt.Sprintf("parser %s is already registered", name)) 31 | } 32 | Parsers[name] = parser 33 | } 34 | -------------------------------------------------------------------------------- /ext/kernelparser/windows/windows.go: -------------------------------------------------------------------------------- 1 | package windows 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stackrox/scanner/database" 7 | "github.com/stackrox/scanner/ext/kernelparser" 8 | ) 9 | 10 | func init() { 11 | kernelparser.RegisterParser("windows", parser) 12 | } 13 | 14 | func parser(_ database.Datastore, _, osImage string) (*kernelparser.ParseMatch, error) { 15 | if strings.Contains(osImage, "windows") { 16 | return nil, kernelparser.ErrKernelUnsupported 17 | } 18 | return nil, kernelparser.ErrKernelUnrecognized 19 | } 20 | -------------------------------------------------------------------------------- /ext/versionfmt/apk/apk.go: -------------------------------------------------------------------------------- 1 | package apk 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | 7 | version "github.com/knqyf263/go-apk-version" 8 | "github.com/stackrox/scanner/ext/versionfmt" 9 | ) 10 | 11 | // ParserName is the name by which the apk parser is registered. 12 | const ParserName = "apk" 13 | 14 | type parser struct{} 15 | 16 | func (p parser) Valid(str string) bool { 17 | _, err := version.NewVersion(str) 18 | return err == nil 19 | } 20 | 21 | // Compare function compares two Alpine package versions 22 | func (p parser) Compare(a, b string) (int, error) { 23 | // Quick check 24 | if a == b { 25 | return 0, nil 26 | } 27 | 28 | a = strings.TrimSpace(a) 29 | if a == "" { 30 | return 0, errors.New("version string is empty") 31 | } 32 | b = strings.TrimSpace(b) 33 | if b == "" { 34 | return 0, errors.New("version string is empty") 35 | } 36 | 37 | if a == versionfmt.MinVersion || b == versionfmt.MaxVersion { 38 | return -1, nil 39 | } 40 | if b == versionfmt.MinVersion || a == versionfmt.MaxVersion { 41 | return 1, nil 42 | } 43 | 44 | v1, err := version.NewVersion(a) 45 | if err != nil { 46 | return 0, nil 47 | } 48 | 49 | v2, err := version.NewVersion(b) 50 | if err != nil { 51 | return 0, nil 52 | } 53 | 54 | return v1.Compare(v2), nil 55 | } 56 | 57 | func (p parser) Namespaces() []string { 58 | return []string{"alpine"} 59 | } 60 | 61 | func init() { 62 | versionfmt.RegisterParser(ParserName, parser{}) 63 | } 64 | -------------------------------------------------------------------------------- /ext/versionfmt/apk/apk_test.go: -------------------------------------------------------------------------------- 1 | package apk 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCompare(t *testing.T) { 11 | cases := []struct { 12 | a, b string 13 | value int 14 | }{ 15 | { 16 | a: "", 17 | b: "", 18 | value: 0, 19 | }, 20 | { 21 | a: "0.7.2-r3", 22 | b: "0.7.2-r3", 23 | value: 0, 24 | }, 25 | { 26 | a: "0.7.2-r3", 27 | b: "0.7.2-r4", 28 | value: -1, 29 | }, 30 | { 31 | a: "0.7.2-r3", 32 | b: "0.7.3-r3", 33 | value: -1, 34 | }, 35 | { 36 | a: "0.7.2-r3", 37 | b: "0.8.2-r3", 38 | value: -1, 39 | }, 40 | { 41 | a: "0.7.2-r3", 42 | b: "1.7.2-r3", 43 | value: -1, 44 | }, 45 | { 46 | a: "0.7.2-r3", 47 | b: "1.7.2-r3", 48 | value: -1, 49 | }, 50 | { 51 | a: "1.2.2_pre2-r0", 52 | b: "1.2.2-r0", 53 | value: -1, 54 | }, 55 | } 56 | 57 | p := parser{} 58 | for _, c := range cases { 59 | t.Run(fmt.Sprintf("%s+%s", c.a, c.b), func(t *testing.T) { 60 | value, err := p.Compare(c.a, c.b) 61 | assert.NoError(t, err) 62 | assert.Equal(t, c.value, value) 63 | 64 | // Flip and ensure that the opposite works as well 65 | value, err = p.Compare(c.b, c.a) 66 | assert.NoError(t, err) 67 | assert.Equal(t, c.value*-1, value) 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ext/versionfmt/language/parser.go: -------------------------------------------------------------------------------- 1 | package language 2 | 3 | import ( 4 | "github.com/facebookincubator/nvdtools/cvefeed/nvd" 5 | "github.com/stackrox/scanner/ext/versionfmt" 6 | ) 7 | 8 | // ParserName is the name by which the language parser is registered. 9 | const ParserName = "language" 10 | 11 | type parser struct{} 12 | 13 | func (p parser) Valid(_ string) bool { 14 | panic("required function not implemented") 15 | } 16 | 17 | func (p parser) Compare(a, b string) (int, error) { 18 | return nvd.SmartVerCmp(a, b), nil 19 | } 20 | 21 | func (p parser) Namespaces() []string { 22 | return []string{"language"} 23 | } 24 | 25 | func init() { 26 | versionfmt.RegisterParser(ParserName, parser{}) 27 | } 28 | -------------------------------------------------------------------------------- /ext/versionfmt/test/driver_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stackrox/scanner/ext/versionfmt" 7 | "github.com/stretchr/testify/assert" 8 | 9 | // Register the following parsers. 10 | _ "github.com/stackrox/scanner/ext/versionfmt/apk" 11 | _ "github.com/stackrox/scanner/ext/versionfmt/dpkg" 12 | _ "github.com/stackrox/scanner/ext/versionfmt/rpm" 13 | ) 14 | 15 | func TestGetVersionFormatForNamespace(t *testing.T) { 16 | assert.Equal(t, "apk", versionfmt.GetVersionFormatForNamespace("alpine:v3.15")) 17 | assert.Equal(t, "apk", versionfmt.GetVersionFormatForNamespace("alpine")) 18 | 19 | assert.Equal(t, "dpkg", versionfmt.GetVersionFormatForNamespace("debian:11")) 20 | assert.Equal(t, "dpkg", versionfmt.GetVersionFormatForNamespace("debian")) 21 | assert.Equal(t, "dpkg", versionfmt.GetVersionFormatForNamespace("ubuntu:14.10")) 22 | assert.Equal(t, "dpkg", versionfmt.GetVersionFormatForNamespace("ubuntu")) 23 | 24 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("amzn:2018.03")) 25 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("amzn")) 26 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("centos:7")) 27 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("centos")) 28 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("rhel:8")) 29 | assert.Equal(t, "rpm", versionfmt.GetVersionFormatForNamespace("rhel")) 30 | 31 | assert.Equal(t, "", versionfmt.GetVersionFormatForNamespace(":")) 32 | } 33 | -------------------------------------------------------------------------------- /ext/vulnmdsrc/appender.go: -------------------------------------------------------------------------------- 1 | package vulnmdsrc 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stackrox/scanner/database" 7 | "github.com/stackrox/scanner/ext/vulnmdsrc/nvd" 8 | "github.com/stackrox/scanner/ext/vulnmdsrc/redhat" 9 | "github.com/stackrox/scanner/ext/vulnmdsrc/types" 10 | ) 11 | 12 | // Appenders returns a slice of each Appender singleton. 13 | func Appenders() []types.Appender { 14 | return []types.Appender{ 15 | nvd.SingletonAppender(), 16 | redhat.SingletonAppender(), 17 | } 18 | } 19 | 20 | // AppenderForVuln returns the appropriate Appender based on the given vulnerability. 21 | func AppenderForVuln(vuln *database.Vulnerability) types.Appender { 22 | if strings.HasPrefix(vuln.Namespace.Name, "centos") { 23 | return redhat.SingletonAppender() 24 | } 25 | 26 | return nvd.SingletonAppender() 27 | } 28 | -------------------------------------------------------------------------------- /ext/vulnmdsrc/nvd/singleton.go: -------------------------------------------------------------------------------- 1 | package nvd 2 | 3 | import ( 4 | "github.com/stackrox/scanner/ext/vulnmdsrc/types" 5 | ) 6 | 7 | var ( 8 | nvdAppender = &appender{} 9 | ) 10 | 11 | // SingletonAppender returns the instance of the NVD appender. 12 | func SingletonAppender() types.Appender { 13 | return nvdAppender 14 | } 15 | -------------------------------------------------------------------------------- /ext/vulnmdsrc/nvd/testdata/nvd_test_incorrect_format.json: -------------------------------------------------------------------------------- 1 | Not a JSON file. 2 | -------------------------------------------------------------------------------- /ext/vulnmdsrc/redhat/singleton.go: -------------------------------------------------------------------------------- 1 | package redhat 2 | 3 | import ( 4 | "github.com/stackrox/scanner/ext/vulnmdsrc/types" 5 | ) 6 | 7 | var ( 8 | redhatAppender = &appender{} 9 | ) 10 | 11 | // SingletonAppender returns the instance of the Red Hat appender. 12 | func SingletonAppender() types.Appender { 13 | return redhatAppender 14 | } 15 | -------------------------------------------------------------------------------- /ext/vulnmdsrc/redhat/testdata/redhat_test_incorrect_format.json: -------------------------------------------------------------------------------- 1 | Not a JSON file. -------------------------------------------------------------------------------- /ext/vulnmdsrc/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/stackrox/scanner/database" 5 | ) 6 | 7 | // MetadataEnricher defines functions used for enriching metadata. 8 | type MetadataEnricher interface { 9 | Metadata() interface{} 10 | Summary() string 11 | } 12 | 13 | // AppendFunc is the type of a callback provided to an Appender. 14 | type AppendFunc func(metadataKey string, metadata MetadataEnricher, severity database.Severity) 15 | 16 | // Appender represents anything that can fetch vulnerability metadata and 17 | // append it to a Vulnerability. 18 | type Appender interface { 19 | // BuildCache loads metadata into memory such that it can be quickly accessed 20 | // for future calls to Append. 21 | BuildCache(dumpDir string) error 22 | 23 | // Append adds metadata to the given database.Vulnerability. 24 | Append(name string, subCVEs []string, callback AppendFunc) error 25 | 26 | // PurgeCache deallocates metadata from memory after all calls to Append are 27 | // finished. 28 | PurgeCache() 29 | 30 | // Name returns the name of the appender 31 | Name() string 32 | } 33 | -------------------------------------------------------------------------------- /ext/vulnsrc/all/all.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | // Import all the vulnsrc providers EXCEPT for the manual source. 5 | _ "github.com/stackrox/scanner/ext/vulnsrc/alpine" 6 | _ "github.com/stackrox/scanner/ext/vulnsrc/amzn" 7 | _ "github.com/stackrox/scanner/ext/vulnsrc/debian" 8 | _ "github.com/stackrox/scanner/ext/vulnsrc/rhel" 9 | _ "github.com/stackrox/scanner/ext/vulnsrc/stackrox" 10 | _ "github.com/stackrox/scanner/ext/vulnsrc/ubuntu" 11 | ) 12 | -------------------------------------------------------------------------------- /ext/vulnsrc/alpine/alpine_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package alpine 16 | 17 | import ( 18 | "path/filepath" 19 | "runtime" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | func TestYAMLParsing(t *testing.T) { 27 | _, filename, _, _ := runtime.Caller(0) 28 | path := filepath.Join(filepath.Dir(filename)) 29 | secdb, err := newSecDB(filepath.Join(path, "/testdata/v34_main.yaml")) 30 | require.Nil(t, err) 31 | vulns := secdb.Vulnerabilities() 32 | 33 | assert.Equal(t, 105, len(vulns)) 34 | assert.Equal(t, "CVE-2016-5387", vulns[0].Name) 35 | assert.Equal(t, "alpine:v3.4", vulns[0].Namespace.Name) 36 | assert.Equal(t, "alpine:v3.4", vulns[0].FixedIn[0].Feature.Namespace.Name) 37 | assert.Equal(t, "apache2", vulns[0].FixedIn[0].Feature.Name) 38 | assert.Equal(t, "https://www.cve.org/CVERecord?id=CVE-2016-5387", vulns[0].Link) 39 | } 40 | -------------------------------------------------------------------------------- /ext/vulnsrc/amzn/repomd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package amzn implements a vulnerability source updater using 16 | // ALAS (Amazon Linux Security Advisories). 17 | package amzn 18 | 19 | // RepoMd ALAS repo metadata. 20 | type RepoMd struct { 21 | RepoList []Repo `xml:"data"` 22 | } 23 | 24 | // Repo ALAS repository. 25 | type Repo struct { 26 | Type string `xml:"type,attr"` 27 | Location Location `xml:"location"` 28 | } 29 | 30 | // Location ALAs location. 31 | type Location struct { 32 | Href string `xml:"href,attr"` 33 | } 34 | -------------------------------------------------------------------------------- /ext/vulnsrc/amzn/testdata/amazon_linux_1_description_0.txt: -------------------------------------------------------------------------------- 1 | Package updates are available for Amazon Linux AMI that fix the following vulnerabilities: CVE-2011-3192: A flaw was found in the way the Apache HTTP Server handled Range HTTP headers. A remote attacker could use this flaw to cause httpd to use an excessive amount of memory and CPU time via HTTP requests with a specially-crafted Range header. The byterange filter in the Apache HTTP Server 1.3.x, 2.0.x through 2.0.64, and 2.2.x through 2.2.19 allows remote attackers to cause a denial of service (memory and CPU consumption) via a Range header that expresses multiple overlapping ranges, as exploited in the wild in August 2011, a different vulnerability than CVE-2007-0086. -------------------------------------------------------------------------------- /ext/vulnsrc/amzn/testdata/amazon_linux_1_description_1.txt: -------------------------------------------------------------------------------- 1 | Package updates are available for Amazon Linux that fix the following vulnerabilities: CVE-2011-3208: Stack-based buffer overflow in the split_wildmats function in nntpd.c in nntpd in Cyrus IMAP Server before 2.3.17 and 2.4.x before 2.4.11 allows remote attackers to execute arbitrary code via a crafted NNTP command. A buffer overflow flaw was found in the cyrus-imapd NNTP server, nntpd. A remote user able to use the nntpd service could use this flaw to crash the nntpd child process or, possibly, execute arbitrary code with the privileges of the cyrus user. -------------------------------------------------------------------------------- /ext/vulnsrc/amzn/testdata/amazon_linux_2_description_1.txt: -------------------------------------------------------------------------------- 1 | Package updates are available for Amazon Linux 2 that fix the following vulnerabilities: CVE-2017-5715: An industry-wide issue was found in the way many modern microprocessor designs have implemented speculative execution of instructions (a commonly used performance optimization). There are three primary variants of the issue which differ in the way the speculative execution can be exploited. Variant CVE-2017-5715 triggers the speculative execution by utilizing branch target injection. It relies on the presence of a precisely-defined instruction sequence in the privileged code as well as the fact that memory accesses may cause allocation into the microprocessor's data cache even for speculatively executed instructions that never actually commit (retire). As a result, an unprivileged attacker could use this flaw to cross the syscall and guest/host boundaries and read privileged memory by conducting targeted cache side-channel attacks. 1519780: CVE-2017-5715 hw: cpu: speculative execution branch target injection -------------------------------------------------------------------------------- /generated/.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !.gitignore 3 | !/scanner 4 | -------------------------------------------------------------------------------- /image/README.md: -------------------------------------------------------------------------------- 1 | # RHEL based scanner and db images 2 | 3 | The `scanner-rhel` and `scanner-db-rhel` images are defined in `scanner/rhel` and `db/rhel` used for the RedHat marketplace as well as for DoD customers. 4 | 5 | These images are built in an opinionated way based on the DoD Centralized Artifacts Repository (DCAR) requirements outlined [here](https://dccscr.dsop.io/dsop/dccscr/tree/master/contributor-onboarding). 6 | 7 | ## Adding new files to the RHEL based images 8 | 9 | To add a new file to a RHEL image, include it in `create-bundle.sh` script, do not add it to the Dockerfile in the `db/rhel` or `scanner/rhel` directories. 10 | -------------------------------------------------------------------------------- /image/bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /image/db/dump/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /image/db/rhel/.gitignore: -------------------------------------------------------------------------------- 1 | bundle.tar.gz 2 | bundle.tar.gz.sha512 3 | prebuild.sh 4 | 5 | -------------------------------------------------------------------------------- /image/scanner/bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /image/scanner/dump/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !genesis_manifests.json 4 | !README.md 5 | -------------------------------------------------------------------------------- /image/scanner/dump/README.md: -------------------------------------------------------------------------------- 1 | This directory contains all the vulnerability dumps (all untracked on Git). 2 | 3 | There is a `genesis_manifests.json`, which outlines the metadata for all 4 | genesis dumps we currently maintain. The format is as follows: 5 | 6 | ```json 7 | { 8 | "knownGenesisDumps": [ 9 | { 10 | "dumpLocationInGS": "Location of Genesis Dump in GCS (ie: gs://stackrox-scanner-ci-vuln-dump/genesis-YYYYMMDDHHMMSS.zip)", 11 | "timestamp": "'Until' time in the dump's manifest.json", 12 | "baseForOfflineDumps": true, 13 | "uuid": "UUID of the diff between the current state of vulns and the vulns embedded in the image from 'baseForOfflineDumps' (ie. gs://definitions.stackrox.io//diff.zip)", 14 | "config": { 15 | "skipFixableCentOSVulns": true, 16 | "ignoreKubernetesVulns": true 17 | } 18 | } 19 | ] 20 | } 21 | ``` 22 | 23 | Only one entry in the `knownGenesisDumps` list may set `baseForOfflineDumps`. 24 | When it is set to true, the dump located in the entry's `dumpLocationInGS` is used as 25 | the de facto source of all vulnerabilities we track. 26 | 27 | For any of the boolean settings, an omission is the same as `false`. 28 | -------------------------------------------------------------------------------- /image/scanner/rhel/.gitignore: -------------------------------------------------------------------------------- 1 | bundle.tar.gz 2 | bundle.tar.gz.sha512 3 | prebuild.sh 4 | scripts 5 | /THIRD_PARTY_NOTICES/ 6 | -------------------------------------------------------------------------------- /image/scanner/scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | /restore-all-dir-contents 6 | /import-additional-cas 7 | /trust-root-ca 8 | 9 | exec /scanner 10 | -------------------------------------------------------------------------------- /image/scanner/scripts/import-additional-cas: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # The function copies everything that could be found under the path provided in 6 | # the first argument, ignoring files starting with '..' which are created by 7 | # kubernetes secret volume mount process. 8 | copy_existing () { 9 | src=$1 10 | if [ -d "$src" ] && [ "$(ls -A -I "..*" "$src")" ]; then 11 | cp -v -L "$src"/* /etc/pki/ca-trust/source/anchors 12 | else 13 | echo "No certificates found in $src" 14 | fi 15 | } 16 | 17 | copy_existing /usr/local/share/ca-certificates 18 | 19 | # Copy the custom trusted CA bundles injected by the Openshift Network Operator. 20 | copy_existing /etc/pki/injected-ca-trust 21 | 22 | update-ca-trust extract 23 | -------------------------------------------------------------------------------- /image/scanner/scripts/restore-all-dir-contents: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | [ -d /.init-dirs ] || exit 0 6 | 7 | cp -rfP /.init-dirs/* / 8 | -------------------------------------------------------------------------------- /image/scanner/scripts/save-dir-contents: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | if [ $# -lt 1 ]; then 6 | echo >&2 "Usage: $0 " 7 | exit 1 8 | fi 9 | 10 | for dir in "$@"; do 11 | dir_no_leading_slash="${dir#/}" 12 | 13 | if [ ${#dir} -eq ${#dir_no_leading_slash} ]; then 14 | echo >&2 "Directory must be an absolute path, is: ${dir}" 15 | exit 1 16 | fi 17 | 18 | target_dir="/.init-dirs/${dir_no_leading_slash}" 19 | mkdir -p "${target_dir}" 20 | cp -rpP "${dir}"/* "${target_dir}" 21 | done 22 | -------------------------------------------------------------------------------- /image/scanner/scripts/trust-root-ca: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | CA_PATH="/run/secrets/stackrox.io/certs/ca.pem" 6 | 7 | # For RHEL 8 | cp "${CA_PATH}" /etc/pki/ca-trust/source/anchors/root-ca.pem 9 | update-ca-trust 10 | -------------------------------------------------------------------------------- /image/vulnerabilities/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_REGISTRY=registry.access.redhat.com 2 | ARG BASE_IMAGE=ubi8-minimal 3 | ARG BASE_TAG=latest 4 | 5 | FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} 6 | 7 | ARG LABEL_VERSION 8 | ARG LABEL_RELEASE 9 | ARG QUAY_TAG_EXPIRATION 10 | 11 | LABEL name="definitions" \ 12 | vendor="StackRox" \ 13 | maintainer="https://stackrox.io/" \ 14 | summary="Vulnerability definitions for the StackRox Security Platform" \ 15 | description="This image contains vulnerability definitions used by StackRox scanners." \ 16 | version="${LABEL_VERSION}" \ 17 | release="${LABEL_RELEASE}" \ 18 | quay.expires-after="${QUAY_TAG_EXPIRATION}" 19 | 20 | COPY scanner-vuln-updates.zip /srv/scanner-vuln-updates.zip 21 | 22 | # This is equivalent to nobody:nobody. 23 | USER 65534:65534 24 | -------------------------------------------------------------------------------- /img/Logo-Red_Hat-Certified_Technology-Vulnerability_Scanner-A-Red-RGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/img/Logo-Red_Hat-Certified_Technology-Vulnerability_Scanner-A-Red-RGB.png -------------------------------------------------------------------------------- /istio/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/stackrox/istio-cves/types" 5 | "github.com/stackrox/scanner/pkg/cache" 6 | ) 7 | 8 | // Cache defines a Istio vulnerability cache. 9 | type Cache interface { 10 | GetVulnsByVersion(version string) []types.Vuln 11 | 12 | cache.Cache 13 | } 14 | -------------------------------------------------------------------------------- /istio/cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestCache(t *testing.T) { 11 | cache := New() 12 | require.NoError(t, cache.LoadFromDirectory("./testdata/before")) 13 | vulns := cache.GetVulnsByVersion("1.13.6") 14 | assert.Equal(t, 1, len(vulns)) 15 | assert.Equal(t, vulns[0].CVSS.ScoreV3, 5.9) 16 | // Out of range. 17 | assert.Empty(t, cache.GetVulnsByVersion("1.15.6")) 18 | 19 | // Update cache. 20 | require.NoError(t, cache.LoadFromDirectory("./testdata/after")) 21 | vulns2 := cache.GetVulnsByVersion("1.13.6") 22 | assert.Equal(t, 1, len(vulns2)) 23 | assert.Equal(t, vulns2[0].CVSS.ScoreV3, 7.5) 24 | } 25 | -------------------------------------------------------------------------------- /istio/cache/singleton.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/stackrox/rox/pkg/utils" 9 | ) 10 | 11 | var ( 12 | once sync.Once 13 | instance Cache 14 | ) 15 | 16 | // Singleton returns the cache instance to use. 17 | func Singleton() Cache { 18 | once.Do(func() { 19 | instance = New() 20 | 21 | if definitionsDir := os.Getenv("ISTIO_DEFINITIONS_DIR"); definitionsDir != "" { 22 | log.Info("Loading Istio definitions into cache") 23 | utils.Must(instance.LoadFromDirectory(definitionsDir)) 24 | log.Info("Done loading Istio definitions into cache") 25 | } 26 | }) 27 | return instance 28 | } 29 | -------------------------------------------------------------------------------- /istio/cache/testdata/after/ISTIO-SECURITY-2022-123.yaml: -------------------------------------------------------------------------------- 1 | name: ISTIO-SECURITY-2022-123 2 | link: https://istio.io/latest/news/security/istio-security-2022-123/ 3 | published: 2022-07-26T00:00Z 4 | description: Ill-formed headers sent to Envoy in certain configurations can lead to unexpected memory access resulting in undefined behavior or crashing 5 | cvss: 6 | scoreV3: 7.5 7 | vectorV3: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H 8 | affected: 9 | - range: ">= 1.13.6, < 1.13.7" 10 | fixedBy: "1.13.7" 11 | - range: ">= 1.14.2, < 1.14.3" 12 | fixedBy: "1.14.3" 13 | -------------------------------------------------------------------------------- /istio/cache/testdata/before/ISTIO-SECURITY-2022-123.yaml: -------------------------------------------------------------------------------- 1 | name: ISTIO-SECURITY-2022-123 2 | link: https://istio.io/latest/news/security/istio-security-2022-123/ 3 | published: 2022-07-26T00:00Z 4 | description: Ill-formed headers sent to Envoy in certain configurations can lead to unexpected memory access resulting in undefined behavior or crashing 5 | cvss: 6 | scoreV3: 5.9 7 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H 8 | affected: 9 | - range: ">= 1.13.6, < 1.13.7" 10 | fixedBy: "1.13.7" 11 | - range: ">= 1.14.2, < 1.14.3" 12 | fixedBy: "1.14.3" 13 | -------------------------------------------------------------------------------- /k8s/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/stackrox/k8s-cves/pkg/validation" 5 | "github.com/stackrox/scanner/pkg/cache" 6 | ) 7 | 8 | // Cache defines a Kubernetes vulnerability cache. 9 | type Cache interface { 10 | GetVulnsByComponent(component, version string) []*validation.CVESchema 11 | 12 | cache.Cache 13 | } 14 | -------------------------------------------------------------------------------- /k8s/cache/singleton.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/stackrox/rox/pkg/utils" 9 | ) 10 | 11 | var ( 12 | once sync.Once 13 | instance Cache 14 | ) 15 | 16 | // Singleton returns the cache instance to use. 17 | func Singleton() Cache { 18 | once.Do(func() { 19 | instance = New() 20 | 21 | if definitionsDir := os.Getenv("K8S_DEFINITIONS_DIR"); definitionsDir != "" { 22 | log.Info("Loading Kubernetes definitions into cache") 23 | utils.Must(instance.LoadFromDirectory(definitionsDir)) 24 | log.Info("Done loading Kubernetes definitions into cache") 25 | } 26 | }) 27 | return instance 28 | } 29 | -------------------------------------------------------------------------------- /k8s/cache/testdata/after/CVE-2020-1234.yaml: -------------------------------------------------------------------------------- 1 | cve: CVE-2020-1234 2 | url: https://nvd.nist.gov/vuln/detail/CVE-2020-12324 3 | issueUrl: https://github.com/kubernetes/kubernetes/issues/1234 4 | description: Simple test 5 | components: 6 | - kube-proxy 7 | - kubelet 8 | cvss: 9 | nvd: 10 | scoreV2: 3.5 11 | vectorV2: AV:N/AC:M/Au:S/C:P/I:N/A:N 12 | scoreV3: 7.7 13 | vectorV3: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N 14 | kubernetes: 15 | scoreV3: 6.3 16 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N 17 | affected: 18 | - range: "<= 1.15.11" 19 | fixedBy: "1.15.12" 20 | - range: ">= 1.16.0, <= 1.16.8" 21 | fixedBy: "1.16.9" 22 | - range: ">= 1.17.0, <= 1.17.4" 23 | - fixedBy: "1.17.5" 24 | - range: "1.18.0" 25 | fixedBy: "1.18.1" 26 | -------------------------------------------------------------------------------- /k8s/cache/testdata/after/CVE-2020-1236.yaml: -------------------------------------------------------------------------------- 1 | cve: CVE-2020-1236 2 | url: https://nvd.nist.gov/vuln/detail/CVE-2020-12326 3 | issueUrl: https://github.com/kubernetes/kubernetes/issues/1236 4 | description: Simple test 2 5 | components: 6 | - kubectl 7 | - kubelet 8 | cvss: 9 | nvd: 10 | scoreV2: 3.5 11 | vectorV2: AV:N/AC:M/Au:S/C:P/I:N/A:N 12 | scoreV3: 7.7 13 | vectorV3: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N 14 | kubernetes: 15 | scoreV3: 6.3 16 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N 17 | affected: 18 | - range: "<= 1.15.11" 19 | fixedBy: "1.15.12" 20 | - range: ">= 1.16.0, <= 1.16.8" 21 | fixedBy: "1.16.9" 22 | - range: ">= 1.17.0, <= 1.17.4" 23 | - fixedBy: "1.17.5" 24 | - range: "1.18.0" 25 | fixedBy: "1.18.1" 26 | -------------------------------------------------------------------------------- /k8s/cache/testdata/after/CVE-2020-1238.yaml: -------------------------------------------------------------------------------- 1 | cve: CVE-2020-1238 2 | url: https://nvd.nist.gov/vuln/detail/CVE-2020-1238 3 | issueUrl: https://github.com/kubernetes/kubernetes/issues/1238 4 | description: Simple test 3 5 | cvss: 6 | nvd: 7 | scoreV2: 3.5 8 | vectorV2: AV:N/AC:M/Au:S/C:P/I:N/A:N 9 | scoreV3: 7.7 10 | vectorV3: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N 11 | kubernetes: 12 | scoreV3: 6.3 13 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N 14 | affected: 15 | - range: "<= 1.15.11" 16 | fixedBy: "1.15.12" 17 | - range: ">= 1.16.0, <= 1.16.8" 18 | fixedBy: "1.16.9" 19 | - range: ">= 1.17.0, <= 1.17.4" 20 | - fixedBy: "1.17.5" 21 | - range: "1.18.0" 22 | fixedBy: "1.18.1" 23 | -------------------------------------------------------------------------------- /k8s/cache/testdata/before/CVE-2020-1234.yaml: -------------------------------------------------------------------------------- 1 | cve: CVE-2020-1234 2 | url: https://nvd.nist.gov/vuln/detail/CVE-2020-12324 3 | issueUrl: https://github.com/kubernetes/kubernetes/issues/1234 4 | description: Simple test 5 | components: 6 | - kube-proxy 7 | - kubelet 8 | cvss: 9 | nvd: 10 | scoreV2: 3.5 11 | vectorV2: AV:N/AC:M/Au:S/C:P/I:N/A:N 12 | scoreV3: 6.3 13 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N 14 | kubernetes: 15 | scoreV3: 6.3 16 | vectorV3: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N 17 | affected: 18 | - range: "<= 1.15.11" 19 | fixedBy: "1.15.12" 20 | - range: ">= 1.16.0, <= 1.16.8" 21 | fixedBy: "1.16.9" 22 | - range: ">= 1.17.0, <= 1.17.4" 23 | - fixedBy: "1.17.5" 24 | - range: "1.18.0" 25 | fixedBy: "1.18.1" 26 | -------------------------------------------------------------------------------- /pkg/analyzer/analyzertest/fake_file.go: -------------------------------------------------------------------------------- 1 | package analyzertest 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "path" 8 | "time" 9 | ) 10 | 11 | // FakeFile is a fake file for testing purposes. 12 | type FakeFile interface { 13 | FullPath() string 14 | FileInfo() os.FileInfo 15 | Contents() io.ReaderAt 16 | } 17 | 18 | // NewFakeFile creates a new fake file from the given path and contents. 19 | func NewFakeFile(fullPath string, contents []byte, mode os.FileMode) FakeFile { 20 | return fakeFile{ 21 | fullPath: fullPath, 22 | contents: contents, 23 | mode: mode, 24 | } 25 | } 26 | 27 | type fakeFile struct { 28 | fullPath string 29 | contents []byte 30 | mode os.FileMode 31 | } 32 | 33 | func (f fakeFile) FullPath() string { 34 | return f.fullPath 35 | } 36 | 37 | func (f fakeFile) FileInfo() os.FileInfo { 38 | return f 39 | } 40 | 41 | func (f fakeFile) Contents() io.ReaderAt { 42 | return bytes.NewReader(f.contents) 43 | } 44 | 45 | func (f fakeFile) Name() string { 46 | return path.Base(f.fullPath) 47 | } 48 | 49 | func (f fakeFile) Mode() os.FileMode { 50 | return f.mode 51 | } 52 | 53 | func (f fakeFile) IsDir() bool { 54 | return false 55 | } 56 | 57 | func (f fakeFile) Sys() interface{} { 58 | return nil 59 | } 60 | 61 | func (f fakeFile) Size() int64 { 62 | return int64(len(f.contents)) 63 | } 64 | 65 | func (f fakeFile) ModTime() time.Time { 66 | return time.Now() 67 | } 68 | -------------------------------------------------------------------------------- /pkg/analyzer/detection/errors.go: -------------------------------------------------------------------------------- 1 | package detection 2 | 3 | import "github.com/stackrox/scanner/pkg/commonerr" 4 | 5 | var ( 6 | // ErrUnsupported is the error that should be raised when an OS or package 7 | // manager is not supported. 8 | ErrUnsupported = commonerr.NewBadRequestError("worker: OS and/or package manager are not supported") 9 | 10 | // ErrParentUnknown is the error that should be raised when a parent layer 11 | // has yet to be processed for the current layer. 12 | ErrParentUnknown = commonerr.NewBadRequestError("worker: parent layer is unknown, it must be processed first") 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/analyzer/dotnetcoreruntime/analyzer_test.go: -------------------------------------------------------------------------------- 1 | package dotnetcoreruntime 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stackrox/scanner/pkg/fsutil/fileinfo/mock" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMatching(t *testing.T) { 11 | a := Analyzer() 12 | cs := a.ProcessFile("/usr/share/dotnet/shared/Microsoft.AspNetCore.App/3.1.8/", mock.NewFileInfo(), nil) 13 | assert.Len(t, cs, 1) 14 | cs = a.ProcessFile("/usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.8/", mock.NewFileInfo(), nil) 15 | assert.Len(t, cs, 1) 16 | cs = a.ProcessFile("/usr/share/dotnet/shared/Hello/3.1.8/", mock.NewFileInfo(), nil) 17 | assert.Empty(t, cs) 18 | cs = a.ProcessFile("/usr/share/dotnet/shared/Microsoft.NETCore.App/3.1/", mock.NewFileInfo(), nil) 19 | assert.Empty(t, cs) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/analyzer/gem/analyzer.go: -------------------------------------------------------------------------------- 1 | package gem 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "regexp" 7 | 8 | "github.com/stackrox/scanner/pkg/analyzer" 9 | "github.com/stackrox/scanner/pkg/component" 10 | ) 11 | 12 | var ( 13 | gemSpecRegexp = regexp.MustCompile(`.*specifications/.*\.gemspec`) 14 | ) 15 | 16 | type analyzerImpl struct{} 17 | 18 | func (analyzerImpl) ProcessFile(fullPath string, fi os.FileInfo, contents io.ReaderAt) []*component.Component { 19 | if !match(fullPath) { 20 | return nil 21 | } 22 | if c := parseGemSpec(fullPath, io.NewSectionReader(contents, 0, fi.Size())); c != nil { 23 | return []*component.Component{c} 24 | } 25 | return nil 26 | } 27 | 28 | func match(fullPath string) bool { 29 | return gemSpecRegexp.MatchString(fullPath) 30 | } 31 | 32 | // Analyzer returns a Ruby Gem analyzer. 33 | func Analyzer() analyzer.Analyzer { 34 | return analyzerImpl{} 35 | } 36 | -------------------------------------------------------------------------------- /pkg/analyzer/gem/analyzer_test.go: -------------------------------------------------------------------------------- 1 | package gem 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stackrox/scanner/pkg/analyzer/analyzertest" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMatching(t *testing.T) { 11 | a := Analyzer() 12 | f := analyzertest.NewFakeFile("usr/local/bundle/specifications/rails-4.2.5.1.gemspec", []byte(validRailsSpec), 0644) 13 | cs := a.ProcessFile(f.FullPath(), f.FileInfo(), f.Contents()) 14 | assert.Len(t, cs, 1) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/analyzer/internal/common/has_name_version.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/stackrox/rox/pkg/stringutils" 5 | "github.com/stackrox/scanner/pkg/component" 6 | ) 7 | 8 | // HasNameAndVersion is a utility function that returns whether 9 | // the passed component has a name and version. 10 | // It is safe to pass a nil-component; this function will return false. 11 | func HasNameAndVersion(c *component.Component) bool { 12 | return c != nil && stringutils.AllNotEmpty(c.Name, c.Version) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/analyzer/java/analyzer.go: -------------------------------------------------------------------------------- 1 | package java 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/stackrox/rox/pkg/set" 9 | "github.com/stackrox/rox/pkg/stringutils" 10 | "github.com/stackrox/scanner/pkg/analyzer" 11 | "github.com/stackrox/scanner/pkg/component" 12 | ) 13 | 14 | var knownIgnorePkgs = set.NewFrozenStringSet("rt", "root") 15 | 16 | type analyzerImpl struct{} 17 | 18 | func (analyzerImpl) ProcessFile(fullPath string, fi os.FileInfo, contents io.ReaderAt) []*component.Component { 19 | if !match(fullPath) || !fi.Mode().IsRegular() || fi.Size() == 0 { 20 | return nil 21 | } 22 | if filterComponent(filepath.Base(fullPath)) { 23 | return nil 24 | } 25 | 26 | components := parseContents(fullPath, fi, contents) 27 | filteredComponents := components[:0] 28 | for _, c := range components { 29 | if knownIgnorePkgs.Contains(c.Name) { 30 | continue 31 | } 32 | addVersion(c) 33 | filteredComponents = append(filteredComponents, c) 34 | } 35 | return filteredComponents 36 | } 37 | 38 | func match(fullPath string) bool { 39 | return javaRegexp.MatchString(fullPath) 40 | } 41 | 42 | func addVersion(c *component.Component) { 43 | if c.JavaPkgMetadata == nil { 44 | return 45 | } 46 | c.Version = stringutils.FirstNonEmpty(c.JavaPkgMetadata.MavenVersion, c.JavaPkgMetadata.ImplementationVersion, c.JavaPkgMetadata.SpecificationVersion) 47 | } 48 | 49 | // Analyzer returns the Java analyzer. 50 | func Analyzer() analyzer.Analyzer { 51 | return analyzerImpl{} 52 | } 53 | -------------------------------------------------------------------------------- /pkg/analyzer/npm/analyzer.go: -------------------------------------------------------------------------------- 1 | package npm 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "strings" 7 | 8 | "github.com/stackrox/scanner/pkg/analyzer" 9 | "github.com/stackrox/scanner/pkg/component" 10 | ) 11 | 12 | type analyzerImpl struct{} 13 | 14 | func (analyzerImpl) ProcessFile(fullPath string, fi os.FileInfo, contents io.ReaderAt) []*component.Component { 15 | if !match(fullPath) { 16 | return nil 17 | } 18 | if c := parsePackageJSON(fullPath, fi, contents); c != nil { 19 | return []*component.Component{c} 20 | } 21 | return nil 22 | } 23 | 24 | func match(fullPath string) bool { 25 | // Check for node modules to ensure it is actually a NodeJS module 26 | return (strings.Contains(fullPath, "node_modules") || strings.Contains(fullPath, "nodejs")) && strings.HasSuffix(fullPath, "/package.json") 27 | } 28 | 29 | // Analyzer returns the NPM analyzer. 30 | func Analyzer() analyzer.Analyzer { 31 | return analyzerImpl{} 32 | } 33 | -------------------------------------------------------------------------------- /pkg/archop/archop_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=ArchOp -linecomment"; DO NOT EDIT. 2 | 3 | package archop 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[opInvalid-0] 12 | _ = x[OpEquals-1] 13 | _ = x[OpNotEquals-2] 14 | _ = x[OpPatternMatch-3] 15 | } 16 | 17 | const _ArchOp_name = "invalidequalsnot equalspattern match" 18 | 19 | var _ArchOp_index = [...]uint8{0, 7, 13, 23, 36} 20 | 21 | func (i ArchOp) String() string { 22 | if i >= ArchOp(len(_ArchOp_index)-1) { 23 | return "ArchOp(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | return _ArchOp_name[_ArchOp_index[i]:_ArchOp_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /pkg/bolthelper/bolt.go: -------------------------------------------------------------------------------- 1 | // This code is adapted from https://github.com/boltdb/bolt/blob/master/cmd/bolt/main.go 2 | // which is licensed under the MIT License 3 | 4 | package bolthelper 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "github.com/pkg/errors" 13 | bolt "go.etcd.io/bbolt" 14 | ) 15 | 16 | const ( 17 | dbOpenTimeout = 2 * time.Minute 18 | ) 19 | 20 | // New returns an instance of the persistent BoltDB store 21 | func New(path string) (*bolt.DB, error) { 22 | dirPath := filepath.Dir(path) 23 | if _, err := os.Stat(dirPath); os.IsNotExist(err) { 24 | err = os.MkdirAll(dirPath, 0700) 25 | if err != nil { 26 | return nil, errors.Wrapf(err, "Error creating db path %v", dirPath) 27 | } 28 | } else if err != nil { 29 | return nil, err 30 | } 31 | options := *bolt.DefaultOptions 32 | options.FreelistType = bolt.FreelistMapType 33 | options.Timeout = dbOpenTimeout 34 | db, err := bolt.Open(path, 0600, &options) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return db, nil 40 | } 41 | 42 | // NewTemp creates a new DB, but places it in the host temporary directory. 43 | func NewTemp(dbPath string) (*bolt.DB, error) { 44 | tmpDir, err := os.MkdirTemp("", "") 45 | if err != nil { 46 | return nil, err 47 | } 48 | return New(filepath.Join(tmpDir, strings.ReplaceAll(dbPath, "/", "_"))) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "archive/zip" 5 | "time" 6 | ) 7 | 8 | // Cache is the interface for common cache operations. 9 | type Cache interface { 10 | Dir() string 11 | LoadFromDirectory(definitionsDir string) error 12 | LoadFromZip(zipR *zip.Reader, definitionsDir string) error 13 | GetLastUpdate() time.Time 14 | SetLastUpdate(t time.Time) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/clairify/client/errors.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrorScanNotFound allows for external libraries to act upon the lack of a scan 7 | ErrorScanNotFound = errors.New("error scan not found") 8 | ) 9 | -------------------------------------------------------------------------------- /pkg/clairify/client/metadata/metadata_keys.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "github.com/stackrox/scanner/ext/vulnmdsrc/nvd" 5 | "github.com/stackrox/scanner/ext/vulnmdsrc/redhat" 6 | ) 7 | 8 | const ( 9 | // NVD represents the key to get NVD data in the vuln Metadata 10 | NVD = nvd.AppenderName 11 | // RedHat represents the key to get Red Hat data in the vuln Metadata 12 | RedHat = redhat.AppenderName 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/clairify/fixtures/fixtures.go: -------------------------------------------------------------------------------- 1 | package fixtures 2 | 3 | // GetLayerResponse is self explanatory 4 | const GetLayerResponse = `{ 5 | "Layer": { 6 | "Features": [ 7 | { 8 | "Name": "pcre3", 9 | "Version": "2:8.35-3.3+deb8u4", 10 | "Vulnerabilities": [ 11 | { 12 | "Name": "CVE-2017-16231", 13 | "Link": "https://security-tracker.debian.org/tracker/CVE-2017-16231" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | }` 20 | 21 | // ErrorResponse is self explanatory 22 | const ErrorResponse = ` 23 | { 24 | "Error": { 25 | "Message": "the resource cannot be found" 26 | } 27 | } 28 | ` 29 | 30 | // PostLayerResponse is self explanatory 31 | const PostLayerResponse = ` 32 | { 33 | "Layer":{ 34 | "Name":"sha256:sha", 35 | "Path": "http://registry/v2/library/nginx/blobs/layer", 36 | "Headers": { 37 | "Authorization": "Bearer thisisbearerauth" 38 | }, 39 | "ParentName": "layer2", 40 | "Format": "Docker", 41 | "IndexedByVersion": 3 42 | } 43 | } 44 | ` 45 | 46 | // GetImageResponse is self explanatory 47 | const GetImageResponse = ` 48 | { 49 | "Image": { 50 | "sha": "sha", 51 | "registry": "registry", 52 | "remote": "namespace/repo", 53 | "tag": "tag" 54 | } 55 | } 56 | ` 57 | 58 | // GetVulnDefsMetadata is includes last time vuln defs was updated. 59 | const GetVulnDefsMetadata = ` 60 | { 61 | "lastUpdatedTime": "2020-12-08T19:05:20.491528869Z" 62 | } 63 | ` 64 | -------------------------------------------------------------------------------- /pkg/clairify/server/middleware/slim_mode.go: -------------------------------------------------------------------------------- 1 | // Any changes to this file should be considered for its counterpart: 2 | // api/grpc/slim_mode_interceptor.go 3 | 4 | package middleware 5 | 6 | import ( 7 | "net/http" 8 | 9 | "github.com/gorilla/mux" 10 | "github.com/pkg/errors" 11 | "github.com/stackrox/rox/pkg/httputil" 12 | "github.com/stackrox/rox/pkg/set" 13 | "github.com/stackrox/scanner/pkg/env" 14 | "google.golang.org/grpc/codes" 15 | ) 16 | 17 | var ( 18 | errSlimMode = errors.New("request not available in slim-mode") 19 | 20 | slimModeAllowList = set.NewFrozenStringSet( 21 | "/clairify/ping", 22 | "/scanner/ping", 23 | ) 24 | ) 25 | 26 | // SlimMode returns middleware which only allows the request to continue when NOT in slim-mode. 27 | func SlimMode() mux.MiddlewareFunc { 28 | slimMode := env.SlimMode.Enabled() 29 | 30 | return func(next http.Handler) http.Handler { 31 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 32 | if slimMode && !slimModeAllowList.Contains(r.RequestURI) { 33 | httputil.WriteGRPCStyleError(w, codes.NotFound, errSlimMode) 34 | return 35 | } 36 | 37 | next.ServeHTTP(w, r) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/clairify/server/middleware/verify_peer_certs.go: -------------------------------------------------------------------------------- 1 | // Any changes to this file should be considered for its counterpart: 2 | // api/grpc/verify_peer_certs_interceptor.go 3 | 4 | package middleware 5 | 6 | import ( 7 | "net/http" 8 | 9 | "github.com/gorilla/mux" 10 | "github.com/stackrox/rox/pkg/httputil" 11 | "github.com/stackrox/rox/pkg/set" 12 | "github.com/stackrox/scanner/pkg/env" 13 | "github.com/stackrox/scanner/pkg/mtls" 14 | "google.golang.org/grpc/codes" 15 | ) 16 | 17 | var ( 18 | verifyPeerCertsAllowList = set.NewFrozenStringSet( 19 | "/clairify/ping", 20 | "/scanner/ping", 21 | ) 22 | ) 23 | 24 | // VerifyPeerCerts returns http middleware to verify peer certs for certain endpoints. 25 | func VerifyPeerCerts() mux.MiddlewareFunc { 26 | skipPeerValidation := env.SkipPeerValidation.Enabled() 27 | 28 | return func(next http.Handler) http.Handler { 29 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 | if !skipPeerValidation && !verifyPeerCertsAllowList.Contains(r.RequestURI) { 31 | if err := mtls.VerifyPeerCertificate(r.TLS); err != nil { 32 | httputil.WriteGRPCStyleError(w, codes.InvalidArgument, err) 33 | return 34 | } 35 | } 36 | 37 | next.ServeHTTP(w, r) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/clairify/types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestGenerateImageFromString(t *testing.T) { 10 | expectedImage := &Image{ 11 | Registry: "docker.io", 12 | Remote: "library/nginx", 13 | Tag: "1.10", 14 | } 15 | 16 | i, err := GenerateImageFromString("nginx:1.10") 17 | assert.NoError(t, err) 18 | assert.Equal(t, expectedImage, i) 19 | 20 | i, err = GenerateImageFromString("library/nginx:1.10") 21 | assert.NoError(t, err) 22 | assert.Equal(t, expectedImage, i) 23 | 24 | i, err = GenerateImageFromString("docker.io/library/nginx:1.10") 25 | assert.NoError(t, err) 26 | assert.Equal(t, expectedImage, i) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/component/sourcetype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=SourceType"; DO NOT EDIT. 2 | 3 | package component 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[UnsetSourceType-0] 12 | _ = x[GemSourceType-1] 13 | _ = x[JavaSourceType-2] 14 | _ = x[NPMSourceType-3] 15 | _ = x[PythonSourceType-4] 16 | _ = x[DotNetCoreRuntimeSourceType-5] 17 | _ = x[SentinelEndSourceType-6] 18 | } 19 | 20 | const _SourceType_name = "UnsetSourceTypeGemSourceTypeJavaSourceTypeNPMSourceTypePythonSourceTypeDotNetCoreRuntimeSourceTypeSentinelEndSourceType" 21 | 22 | var _SourceType_index = [...]uint8{0, 15, 28, 42, 55, 71, 98, 119} 23 | 24 | func (i SourceType) String() string { 25 | if i < 0 || i >= SourceType(len(_SourceType_index)-1) { 26 | return "SourceType(" + strconv.FormatInt(int64(i), 10) + ")" 27 | } 28 | return _SourceType_name[_SourceType_index[i]:_SourceType_index[i+1]] 29 | } 30 | -------------------------------------------------------------------------------- /pkg/component/type.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | // SourceType represents the specific type of a language-level component. 4 | // 5 | //go:generate stringer -type=SourceType 6 | type SourceType int 7 | 8 | // This block enumerates valid types. 9 | const ( 10 | UnsetSourceType SourceType = iota 11 | GemSourceType 12 | JavaSourceType 13 | NPMSourceType 14 | PythonSourceType 15 | DotNetCoreRuntimeSourceType 16 | 17 | SentinelEndSourceType 18 | ) 19 | -------------------------------------------------------------------------------- /pkg/component/valid.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "github.com/stackrox/rox/pkg/stringutils" 5 | ) 6 | 7 | // Valid returns whether the component is well-formed. 8 | // Analyzers MUST ensure that all components they return pass 9 | // this function. 10 | // Downstream subsystems can rely on this being the case. 11 | func Valid(c *Component) bool { 12 | return stringutils.AllNotEmpty(c.Name, c.Version) && c.SourceType != UnsetSourceType 13 | } 14 | 15 | // FilterToOnlyValid filters the given slice of components to only 16 | // valid ones. It mutates the same underlying array instead of allocating 17 | // a new one. 18 | func FilterToOnlyValid(components []*Component) []*Component { 19 | filtered := components[:0] 20 | for _, c := range components { 21 | if Valid(c) { 22 | filtered = append(filtered, c) 23 | } 24 | } 25 | return filtered 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cpe/attribute_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type Attribute -linecomment"; DO NOT EDIT. 2 | 3 | package cpe 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Part-0] 12 | _ = x[Vendor-1] 13 | _ = x[Product-2] 14 | _ = x[Version-3] 15 | _ = x[Update-4] 16 | _ = x[Edition-5] 17 | _ = x[SwEdition-6] 18 | _ = x[TargetSW-7] 19 | _ = x[TargetHW-8] 20 | _ = x[Language-9] 21 | _ = x[Other-10] 22 | } 23 | 24 | const _Attribute_name = "partvendorproductversionupdateeditionsw_editiontarget_swtarget_hwlanguageother" 25 | 26 | var _Attribute_index = [...]uint8{0, 4, 10, 17, 24, 30, 37, 47, 56, 65, 73, 78} 27 | 28 | func (i Attribute) String() string { 29 | if i < 0 || i >= Attribute(len(_Attribute_index)-1) { 30 | return "Attribute(" + strconv.FormatInt(int64(i), 10) + ")" 31 | } 32 | return _Attribute_name[_Attribute_index[i]:_Attribute_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/cpe/bind.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // Influenced by ClairCore under Apache 2.0 License 3 | // https://github.com/quay/claircore 4 | // Base revision: 2843d93852e5cfc5617c65acbd3c591f64f1d85c 5 | /////////////////////////////////////////////////// 6 | 7 | package cpe 8 | 9 | import "strings" 10 | 11 | // BindFS returns the WFN bound as CPE 2.3 formatted string. 12 | func (w WFN) BindFS() string { 13 | b := strings.Builder{} 14 | b.WriteString(`cpe:2.3`) 15 | for i := 0; i < NumAttr; i++ { 16 | b.WriteByte(':') 17 | w.Attr[i].bind(&b) 18 | } 19 | return b.String() 20 | } 21 | 22 | // Bind binds the value to a formatted string, writing it into the provided 23 | // strings.Builder. 24 | func (v *Value) bind(b *strings.Builder) (err error) { 25 | switch v.Kind { 26 | case ValueUnset, ValueAny: 27 | _, err = b.WriteRune('*') 28 | case ValueNA: 29 | _, err = b.WriteRune('-') 30 | case ValueSet: 31 | _, err = valueString.WriteString(b, v.V) 32 | } 33 | return err 34 | } 35 | 36 | // ValueString does FS character replacing. 37 | var valueString = strings.NewReplacer( 38 | `\.`, `.`, 39 | `\-`, `-`, 40 | `\_`, `_`, 41 | ) 42 | -------------------------------------------------------------------------------- /pkg/cpe/cpe.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // Influenced by ClairCore under Apache 2.0 License 3 | // https://github.com/quay/claircore 4 | // Base revision: 2843d93852e5cfc5617c65acbd3c591f64f1d85c 5 | /////////////////////////////////////////////////// 6 | 7 | // Package cpe provides for handling Common Platform Enumeration (CPE) names. 8 | package cpe 9 | -------------------------------------------------------------------------------- /pkg/cpe/json.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // Influenced by ClairCore under Apache 2.0 License 3 | // https://github.com/quay/claircore 4 | // Base revision: 2843d93852e5cfc5617c65acbd3c591f64f1d85c 5 | /////////////////////////////////////////////////// 6 | 7 | package cpe 8 | 9 | import "errors" 10 | 11 | // MarshalText implements encoding.TextMarshaler. 12 | func (w *WFN) MarshalText() ([]byte, error) { 13 | switch err := w.Valid(); { 14 | case err == nil: 15 | case errors.Is(err, ErrUnset): 16 | return []byte{}, nil 17 | default: 18 | return nil, err 19 | } 20 | return []byte(w.BindFS()), nil 21 | } 22 | 23 | // UnmarshalText implements encoding.TextUnmarshaler. 24 | func (w *WFN) UnmarshalText(b []byte) (err error) { 25 | if len(b) == 0 { 26 | return nil 27 | } 28 | *w, err = Unbind(string(b)) 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /pkg/cpe/sql.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // Influenced by ClairCore under Apache 2.0 License 3 | // https://github.com/quay/claircore 4 | // Base revision: 2843d93852e5cfc5617c65acbd3c591f64f1d85c 5 | /////////////////////////////////////////////////// 6 | 7 | package cpe 8 | 9 | import ( 10 | "database/sql/driver" 11 | "errors" 12 | "fmt" 13 | ) 14 | 15 | // Scan implements sql.Scanner. 16 | func (v *Value) Scan(i interface{}) error { 17 | if i == nil { 18 | return nil 19 | } 20 | s, ok := i.(string) 21 | if !ok { 22 | return fmt.Errorf("cpe: can't scan type %T into Value", i) 23 | } 24 | v.unbindFS(nil, s) 25 | return validate(v.V) 26 | } 27 | 28 | // Value implements driver.Valuer. 29 | func (v Value) Value() (driver.Value, error) { 30 | if err := validate(v.V); err != nil { 31 | return nil, err 32 | } 33 | return v.String(), nil 34 | } 35 | 36 | // Scan implements sql.Scanner. 37 | func (w *WFN) Scan(i interface{}) (err error) { 38 | if i == nil { 39 | return nil 40 | } 41 | s, ok := i.(string) 42 | switch { 43 | case !ok: 44 | return fmt.Errorf("cpe: can't scan type %T into WFN", i) 45 | case s == "": 46 | return nil 47 | } 48 | *w, err = Unbind(s) 49 | return err 50 | } 51 | 52 | // Value implements driver.Valuer. 53 | func (w WFN) Value() (driver.Value, error) { 54 | switch err := w.Valid(); { 55 | case err == nil: 56 | case errors.Is(err, ErrUnset): 57 | return "", nil 58 | default: 59 | return nil, err 60 | } 61 | return w.String(), nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/cpe/valuekind_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type ValueKind"; DO NOT EDIT. 2 | 3 | package cpe 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[ValueUnset-0] 12 | _ = x[ValueAny-1] 13 | _ = x[ValueNA-2] 14 | _ = x[ValueSet-3] 15 | } 16 | 17 | const _ValueKind_name = "ValueUnsetValueAnyValueNAValueSet" 18 | 19 | var _ValueKind_index = [...]uint8{0, 10, 18, 25, 33} 20 | 21 | func (i ValueKind) String() string { 22 | if i >= ValueKind(len(_ValueKind_index)-1) { 23 | return "ValueKind(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | return _ValueKind_name[_ValueKind_index[i]:_ValueKind_index[i+1]] 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cvss/util.go: -------------------------------------------------------------------------------- 1 | package cvss 2 | 3 | import ( 4 | "github.com/stackrox/scanner/database" 5 | "github.com/stackrox/scanner/pkg/types" 6 | ) 7 | 8 | // SeverityFromCVSS converts the CVSS Score (0.0 - 10.0) into a 9 | // database.Severity following the qualitative rating scale available in the 10 | // CVSS v3.0 specification (https://www.first.org/cvss/specification-document), 11 | // Table 14. 12 | // 13 | // The Negligible level is set for CVSS scores between [0, 1), replacing the 14 | // specified None level, originally used for a score of 0. 15 | func SeverityFromCVSS(meta *types.Metadata) database.Severity { 16 | score := meta.CVSSv3.Score 17 | if score == 0 { 18 | score = meta.CVSSv2.Score 19 | } 20 | switch { 21 | case score < 1.0: 22 | return database.NegligibleSeverity 23 | case score < 4.0: 24 | return database.LowSeverity 25 | case score < 7.0: 26 | return database.MediumSeverity 27 | case score < 9.0: 28 | return database.HighSeverity 29 | case score <= 10.0: 30 | return database.CriticalSeverity 31 | } 32 | return database.UnknownSeverity 33 | } 34 | -------------------------------------------------------------------------------- /pkg/elf/testdata/README.md: -------------------------------------------------------------------------------- 1 | # ELF format testing files 2 | - `elf_exec`: A linux executable in ELF format 3 | - `macho_exec`: A MacOS executable in Mach-O format 4 | - `README.md`: A text file 5 | -------------------------------------------------------------------------------- /pkg/elf/testdata/elf_exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/elf/testdata/elf_exec -------------------------------------------------------------------------------- /pkg/elf/testdata/macho_exec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/elf/testdata/macho_exec -------------------------------------------------------------------------------- /pkg/env/booleansetting.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // BooleanSetting represents an environment variable which should be parsed into a boolean 8 | type BooleanSetting interface { 9 | Setting 10 | Enabled() bool 11 | } 12 | 13 | type booleanSetting struct { 14 | Setting 15 | } 16 | 17 | // Enabled returns the bool object represented by the environment variable 18 | func (s *booleanSetting) Enabled() bool { 19 | v, err := strconv.ParseBool(s.Value()) 20 | return v && err == nil 21 | } 22 | 23 | // RegisterBooleanSetting globally registers and returns a new boolean setting. 24 | func RegisterBooleanSetting(envVar string, defaul bool, opts ...SettingOption) BooleanSetting { 25 | return &booleanSetting{ 26 | Setting: RegisterSetting(envVar, append(opts, WithDefault(strconv.FormatBool(defaul)))...), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/env/durationsetting_options.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | type durationSettingOpts struct { 4 | zeroAllowed bool 5 | } 6 | 7 | // DurationSettingOption represents an option which may be specified 8 | // for a DurationSetting environment variable. 9 | type DurationSettingOption interface { 10 | apply(opts *durationSettingOpts) 11 | } 12 | 13 | type durationSettingOptsFunc func(opts *durationSettingOpts) 14 | 15 | func (f durationSettingOptsFunc) apply(opts *durationSettingOpts) { 16 | f(opts) 17 | } 18 | 19 | // WithDurationZeroAllowed specifies the DurationSetting may have a value of 0. 20 | func WithDurationZeroAllowed() DurationSettingOption { 21 | return durationSettingOptsFunc(func(opts *durationSettingOpts) { 22 | opts.zeroAllowed = true 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/env/integersetting.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // IntegerSetting represents an environment variable which should be parsed into an integer. 8 | type IntegerSetting interface { 9 | Setting 10 | Int() int 11 | } 12 | 13 | type integerSetting struct { 14 | Setting 15 | defaultValue int 16 | } 17 | 18 | // Int returns the int object represented by the environment variable. 19 | func (s *integerSetting) Int() int { 20 | v, err := strconv.Atoi(s.Value()) 21 | if err != nil { 22 | return s.defaultValue 23 | } 24 | return v 25 | } 26 | 27 | // RegisterIntegerSetting globally registers and returns a new integer setting. 28 | func RegisterIntegerSetting(envVar string, defaultValue int, opts ...SettingOption) IntegerSetting { 29 | return &integerSetting{ 30 | Setting: RegisterSetting(envVar, append(opts, WithDefault(strconv.Itoa(defaultValue)))...), 31 | defaultValue: defaultValue, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/features/features.go: -------------------------------------------------------------------------------- 1 | // Package features helps enable or disable features. 2 | package features 3 | 4 | import ( 5 | "github.com/stackrox/scanner/pkg/env" 6 | ) 7 | 8 | // A FeatureFlag is a product behavior that can be enabled or disabled using an environment variable. 9 | type FeatureFlag interface { 10 | Name() string 11 | EnvVar() string 12 | Enabled() bool 13 | } 14 | 15 | type feature struct { 16 | env.BooleanSetting 17 | name string 18 | } 19 | 20 | func (f *feature) Name() string { 21 | return f.name 22 | } 23 | 24 | func registerFeature(name, envVar string, defaul bool) FeatureFlag { 25 | return &feature{ 26 | name: name, 27 | BooleanSetting: env.RegisterBooleanSetting(envVar, defaul), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/features/list.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | var ( 4 | // ContinueUnknownOS defines if scanning should continue upon detecting unknown OS. 5 | ContinueUnknownOS = registerFeature("Enable continuation upon detecting unknown OS", "ROX_CONTINUE_UNKNOWN_OS", true) 6 | // RHEL9Scanning enables support for scanning RHEL9-based images. 7 | RHEL9Scanning = registerFeature("Enable support for scanning RHEL9 images", "ROX_RHEL9_SCANNING", true) 8 | ) 9 | -------------------------------------------------------------------------------- /pkg/fsutil/fileinfo/mock/mock.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "os" 5 | "time" 6 | ) 7 | 8 | // fileInfo is a dummy implementation for os.FileInfo. 9 | type fileInfo struct { 10 | mode os.FileMode 11 | } 12 | 13 | // NewFileInfo creates a new mock os.FileInfo. 14 | // By default, the returned *os.FileInfo is a directory. 15 | func NewFileInfo(opts ...FileInfoOption) *fileInfo { 16 | var o options 17 | for _, opt := range opts { 18 | opt.apply(&o) 19 | } 20 | 21 | mode := os.ModeDir 22 | if o.mode != 0 { 23 | mode = o.mode 24 | } 25 | 26 | return &fileInfo{ 27 | mode: mode, 28 | } 29 | } 30 | 31 | func (f *fileInfo) Name() string { 32 | return "" 33 | } 34 | 35 | func (f *fileInfo) Size() int64 { 36 | return 0 37 | } 38 | 39 | func (f *fileInfo) Mode() os.FileMode { 40 | return f.mode 41 | } 42 | 43 | func (f *fileInfo) ModTime() time.Time { 44 | return time.Now() 45 | } 46 | 47 | func (f *fileInfo) IsDir() bool { 48 | return f.Mode().IsDir() 49 | } 50 | 51 | func (f *fileInfo) Sys() interface{} { 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/fsutil/fileinfo/mock/options.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import "os" 4 | 5 | type options struct { 6 | mode os.FileMode 7 | } 8 | 9 | // FileInfoOption represents a configuration option for a mock os.FileInfo. 10 | type FileInfoOption interface { 11 | apply(*options) 12 | } 13 | 14 | type fileInfoOption func(*options) 15 | 16 | func (f fileInfoOption) apply(o *options) { 17 | f(o) 18 | } 19 | 20 | // FileMode sets the file mode. 21 | func FileMode(mode os.FileMode) FileInfoOption { 22 | return fileInfoOption(func(o *options) { 23 | o.mode = mode 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/fsutil/fileinfo/util.go: -------------------------------------------------------------------------------- 1 | package fileinfo 2 | 3 | import "io/fs" 4 | 5 | // IsFileExecutable returns true if the file is an executable regular file. 6 | func IsFileExecutable(fileInfo fs.FileInfo) bool { 7 | return fileInfo.Mode().IsRegular() && fileInfo.Mode()&0111 != 0 8 | } 9 | 10 | // IsFileSymlink returns true if fileInfo represents a symlink. 11 | func IsFileSymlink(fileInfo fs.FileInfo) bool { 12 | return fileInfo.Mode().Type()&fs.ModeSymlink != 0 13 | } 14 | -------------------------------------------------------------------------------- /pkg/fsutil/fsutil_test.go: -------------------------------------------------------------------------------- 1 | package fsutil 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestWithin(t *testing.T) { 11 | testcases := []struct { 12 | parent string 13 | child string 14 | within bool 15 | }{ 16 | { 17 | parent: "/a/b", 18 | child: "/a/b/c", 19 | within: true, 20 | }, 21 | { 22 | parent: "/a/b/c", 23 | child: "/a/b", 24 | within: false, 25 | }, 26 | { 27 | parent: "/a/b", 28 | child: "/a/b", 29 | within: true, 30 | }, 31 | { 32 | parent: "a/b", 33 | child: "a/b", 34 | within: true, 35 | }, 36 | { 37 | parent: "a/b", 38 | child: "/a/b", 39 | within: false, 40 | }, 41 | { 42 | parent: "/a/b", 43 | child: "a/b", 44 | within: false, 45 | }, 46 | { 47 | parent: "/a", 48 | child: "/a/b/../c/..", 49 | within: true, 50 | }, 51 | { 52 | parent: "/a", 53 | child: "/a/b/../..", 54 | within: false, 55 | }, 56 | } 57 | for _, testcase := range testcases { 58 | t.Run(fmt.Sprintf("%s %s", testcase.parent, testcase.child), func(t *testing.T) { 59 | assert.Equal(t, testcase.within, Within(testcase.parent, testcase.child)) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/istioutil/util.go: -------------------------------------------------------------------------------- 1 | package istioutil 2 | 3 | import ( 4 | "github.com/hashicorp/go-version" 5 | "github.com/stackrox/istio-cves/types" 6 | ) 7 | 8 | // IsAffected returns whether the given version of Istio is affected by the given vulnerability. 9 | // If it is, then the fixed-by version is returned as well. 10 | func IsAffected(v *version.Version, vuln types.Vuln) (bool, string, error) { 11 | for _, affected := range vuln.Affected { 12 | constraint, err := version.NewConstraint(affected.Range) 13 | if err != nil { 14 | return false, "", err 15 | } 16 | if constraint.Check(v) { 17 | return true, affected.FixedBy, nil 18 | } 19 | } 20 | 21 | return false, "", nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/metrics/features.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | ) 8 | 9 | var ( 10 | listFeaturesDurationMilliseconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{ 11 | Name: "list_features_duration_millis", 12 | Help: "Time it takes to list all the features in a layer.", 13 | Buckets: []float64{1, 10, 100, 500, 1000}, 14 | }, []string{"packageformat", "step"}) 15 | ) 16 | 17 | func init() { 18 | prometheus.MustRegister(listFeaturesDurationMilliseconds) 19 | } 20 | 21 | // ObserveListFeaturesTime observes `ListFeatures` for the given package format and sub-step from the given start time. 22 | func ObserveListFeaturesTime(packageformat, step string, start time.Time) { 23 | listFeaturesDurationMilliseconds. 24 | WithLabelValues(packageformat, step). 25 | Observe(float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/nvd/link.go: -------------------------------------------------------------------------------- 1 | package nvd 2 | 3 | import "fmt" 4 | 5 | // Link returns a CVE link to NVD 6 | func Link(cve string) string { 7 | return fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", cve) 8 | } 9 | -------------------------------------------------------------------------------- /pkg/osrelease/osrelease_test.go: -------------------------------------------------------------------------------- 1 | package osrelease 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestGetOSReleaseMap(t *testing.T) { 9 | type args struct { 10 | data []byte 11 | fields []string 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want map[string]string 17 | osReleaseData string 18 | }{ 19 | { 20 | name: "when line that is not sh assignment then ignored", 21 | want: map[string]string{ 22 | "FOO": "bar", 23 | "BAR": "foo", 24 | }, 25 | osReleaseData: ` 26 | FOO=BAR 27 | FOOZ 28 | BAR=foo 29 | `, 30 | }, 31 | { 32 | name: "when empty line that is not sh assignment then ignored", 33 | want: map[string]string{ 34 | "FOO": "bar", 35 | "BAR": "foo", 36 | }, 37 | osReleaseData: ` 38 | FOO=BAR 39 | 40 | BAR=foo 41 | `, 42 | }, 43 | { 44 | name: "when assignment without value then value is empty", 45 | want: map[string]string{ 46 | "FOO": "bar", 47 | "BAR": "", 48 | }, 49 | osReleaseData: ` 50 | FOO=bar 51 | BAR= 52 | `, 53 | }, 54 | } 55 | for _, tt := range tests { 56 | if tt.osReleaseData != "" { 57 | tt.args.data = []byte(tt.osReleaseData) 58 | } 59 | t.Run(tt.name, func(t *testing.T) { 60 | if got := GetOSReleaseMap(tt.args.data, tt.args.fields...); !reflect.DeepEqual(got, tt.want) { 61 | t.Errorf("GetOSReleaseMap() = %v, want %v", got, tt.want) 62 | } 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pkg/ovalutil/test.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////// 2 | // Influenced by ClairCore under Apache 2.0 License 3 | // https://github.com/quay/claircore 4 | /////////////////////////////////////////////////// 5 | 6 | package ovalutil 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/quay/goval-parser/oval" 13 | ) 14 | 15 | var ( 16 | errTestSkip = errors.New("skip this test") 17 | ) 18 | 19 | // TestLookup is a general test lookup function. 20 | // 21 | // The passed function can be used as an allowlist for test kinds. All known 22 | // kinds will be returned if not provided. 23 | func TestLookup(root *oval.Root, ref string, f func(kind string) bool) (oval.Test, error) { 24 | kind, i, err := root.Tests.Lookup(ref) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if f != nil && !f(kind) { 29 | return nil, fmt.Errorf("disallowed kind %q: %w", kind, errTestSkip) 30 | } 31 | switch kind { 32 | case "dpkginfo_test": 33 | return &root.Tests.DpkgInfoTests[i], nil 34 | case "line_test": 35 | return &root.Tests.LineTests[i], nil 36 | case "rpminfo_test": 37 | return &root.Tests.RPMInfoTests[i], nil 38 | case "rpmverifyfile_test": 39 | return &root.Tests.RPMVerifyFileTests[i], nil 40 | case "textfilecontent54_test": 41 | return &root.Tests.TextfileContent54Tests[i], nil 42 | case "uname_test": 43 | return &root.Tests.UnameTests[i], nil 44 | case "version55_test": 45 | return &root.Tests.Version55Tests[i], nil 46 | } 47 | return nil, fmt.Errorf("unknown kind: %q", kind) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/repo2cpe/mapping_test.go: -------------------------------------------------------------------------------- 1 | package repo2cpe 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestMapping(t *testing.T) { 12 | _, filename, _, _ := runtime.Caller(0) 13 | cpesDir := filepath.Join(filepath.Dir(filename), "/testdata") 14 | t.Setenv("REPO_TO_CPE_DIR", cpesDir) 15 | 16 | repos := []string{ 17 | "3scale-amp-2-rpms-for-rhel-8-x86_64-debug-rpms", 18 | "3scale-amp-2-rpms-for-rhel-8-x86_64-rpms", 19 | "rhel-8-for-x86_64-baseos-rpms", 20 | "fakerepo", 21 | } 22 | expectedCPEs := []string{ 23 | "cpe:/a:redhat:3scale:2.13::el8", 24 | "cpe:/a:redhat:3scale_amp:2.10::el8", 25 | "cpe:/a:redhat:3scale_amp:2.11::el8", 26 | "cpe:/a:redhat:3scale_amp:2.12::el8", 27 | "cpe:/a:redhat:3scale_amp:2.8::el8", 28 | "cpe:/a:redhat:3scale_amp:2.9::el8", 29 | "cpe:/o:redhat:enterprise_linux:8::baseos", 30 | "cpe:/o:redhat:rhel:8.3::baseos", 31 | } 32 | 33 | m := Singleton() 34 | cpes := m.Get(repos) 35 | assert.ElementsMatch(t, cpes, expectedCPEs) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/repo2cpe/singleton.go: -------------------------------------------------------------------------------- 1 | package repo2cpe 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/stackrox/rox/pkg/utils" 9 | ) 10 | 11 | var ( 12 | once sync.Once 13 | instance *Mapping 14 | ) 15 | 16 | // Singleton returns the cache instance to use. 17 | func Singleton() *Mapping { 18 | once.Do(func() { 19 | instance = NewMapping() 20 | 21 | if definitionsDir := os.Getenv("REPO_TO_CPE_DIR"); definitionsDir != "" { 22 | log.Info("Loading repo-to-cpe map into mem") 23 | utils.Must(instance.Load(definitionsDir)) 24 | log.Info("Done loading repo-to-cpe map into mem") 25 | } 26 | }) 27 | return instance 28 | } 29 | -------------------------------------------------------------------------------- /pkg/repo2cpe/testdata/repository-to-cpe.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "3scale-amp-2-rpms-for-rhel-8-x86_64-debug-rpms": { 4 | "cpes": [ 5 | "cpe:/a:redhat:3scale:2.13::el8", 6 | "cpe:/a:redhat:3scale_amp:2.10::el8", 7 | "cpe:/a:redhat:3scale_amp:2.11::el8", 8 | "cpe:/a:redhat:3scale_amp:2.12::el8", 9 | "cpe:/a:redhat:3scale_amp:2.8::el8", 10 | "cpe:/a:redhat:3scale_amp:2.9::el8" 11 | ], 12 | "repo_relative_urls": [ 13 | "content/dist/layered/rhel8/x86_64/3scale-amp/2/debug" 14 | ] 15 | }, 16 | "3scale-amp-2-rpms-for-rhel-8-x86_64-rpms": { 17 | "cpes": [ 18 | "cpe:/a:redhat:3scale:2.13::el8", 19 | "cpe:/a:redhat:3scale_amp:2.10::el8", 20 | "cpe:/a:redhat:3scale_amp:2.11::el8", 21 | "cpe:/a:redhat:3scale_amp:2.12::el8", 22 | "cpe:/a:redhat:3scale_amp:2.8::el8", 23 | "cpe:/a:redhat:3scale_amp:2.9::el8" 24 | ], 25 | "repo_relative_urls": [ 26 | "content/dist/layered/rhel8/x86_64/3scale-amp/2/os" 27 | ] 28 | }, 29 | "rhel-8-for-x86_64-baseos-rpms": { 30 | "cpes": [ 31 | "cpe:/o:redhat:enterprise_linux:8::baseos", 32 | "cpe:/o:redhat:rhel:8.3::baseos" 33 | ], 34 | "repo_relative_urls": [ 35 | "content/dist/rhel8/8/x86_64/baseos/os" 36 | ] 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/rhel/layer_name.go: -------------------------------------------------------------------------------- 1 | package rhel 2 | 3 | import "strings" 4 | 5 | const ( 6 | suffix = "uncertified" 7 | ) 8 | 9 | // GetUncertifiedLayerName converts the given layer name to 10 | // be identified as uncertified. 11 | func GetUncertifiedLayerName(layerName string) string { 12 | return layerName + suffix 13 | } 14 | 15 | // GetOriginalLayerName takes an uncertified name 16 | // (usually a name generated by GetUncertifiedLayerName) 17 | // and converts it back to its original name. 18 | func GetOriginalLayerName(layerName string) string { 19 | return strings.TrimSuffix(layerName, suffix) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/rhel/pulp/testdata/PULP_MANIFEST: -------------------------------------------------------------------------------- 1 | RHEL8/openshift-4.1.oval.xml.bz2,b067fe8942118b9dfa7ae24569601d1081e63b7050033953c9154324ff55cc27,12994 2 | RHEL8/openshift-4.2.oval.xml.bz2,b067fe8942118b9dfa7ae24569601d1081e63b7050033953c9154324ff55cc27,12994 3 | RHEL8/openshift-4.3.oval.xml.bz2,b067fe8942118b9dfa7ae24569601d1081e63b7050033953c9154324ff55cc27,12994 4 | RHEL8/openshift-4-including-unpatched.oval.xml.bz2,040a8719cf7b2e5726cd96d642cd5fe6d24381cbd92f0caa99a977296b9070dc,27469 5 | RHEL8/openshift-4.oval.xml.bz2,b067fe8942118b9dfa7ae24569601d1081e63b7050033953c9154324ff55cc27,12994 6 | RHEL8/openshift-service-mesh-1.0.oval.xml.bz2,901b29d7928f55cf1b09928b75f2d6199ce8741c07a79bd010eca23d3fa85155,5364 7 | RHEL8/openshift-service-mesh-1.1.oval.xml.bz2,6367a4bc26671e36bd0cd2bbad72c7f5a4b1f2a49948809684e586bc9233131d,3340 8 | -------------------------------------------------------------------------------- /pkg/rhelv2/rpm/rpm_language_analyzer.go: -------------------------------------------------------------------------------- 1 | package rpm 2 | 3 | import ( 4 | "github.com/stackrox/rox/pkg/stringutils" 5 | "github.com/stackrox/scanner/pkg/analyzer" 6 | "github.com/stackrox/scanner/pkg/component" 7 | "github.com/stackrox/scanner/pkg/rpm" 8 | ) 9 | 10 | // AnnotateComponentsWithPackageManagerInfo checks for each component if it was installed by the package manager, 11 | // and sets the `FromPackageManager` attribute accordingly. 12 | func AnnotateComponentsWithPackageManagerInfo(files analyzer.Files, components []*component.Component) error { 13 | if len(components) == 0 { 14 | return nil 15 | } 16 | rpmDB, err := rpm.CreateDatabaseFromImage(files) 17 | if err != nil { 18 | return err 19 | } 20 | if rpmDB == nil { 21 | return nil 22 | } 23 | defer rpmDB.Delete() 24 | locationAlreadyChecked := make(map[string]bool) 25 | for _, c := range components { 26 | // This handles jar-in-jar cases as the location is manually created so we only want 27 | // the initial path 28 | normalizedLocation := stringutils.GetUpTo(c.Location, ":") 29 | fromPackageManager, ok := locationAlreadyChecked[normalizedLocation] 30 | if ok { 31 | c.FromPackageManager = fromPackageManager 32 | continue 33 | } 34 | c.FromPackageManager = rpmDB.ProvidesFile(normalizedLocation) 35 | locationAlreadyChecked[normalizedLocation] = c.FromPackageManager 36 | } 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/rhelv2/rpm/testdata/Packages: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/rhelv2/rpm/testdata/Packages -------------------------------------------------------------------------------- /pkg/rhelv2/rpm/testdata/repository-to-cpe.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "rhel-8-for-x86_64-baseos-rpms": { 4 | "cpes": [ 5 | "cpe:/o:redhat:enterprise_linux:8::baseos" 6 | ] 7 | }, 8 | "rhel-8-for-x86_64-appstream-rpms": { 9 | "cpes": [ 10 | "cpe:/a:redhat:enterprise_linux:8::appstream" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pkg/rhelv2/rpm/testdata/rpmdb.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/rhelv2/rpm/testdata/rpmdb.sqlite -------------------------------------------------------------------------------- /pkg/rhelv2/rpm/testdata/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_sets": [ 3 | "rhel-8-for-x86_64-baseos-rpms", 4 | "rhel-8-for-x86_64-appstream-rpms" 5 | ], 6 | "image_contents": [], 7 | "metadata": { 8 | "icm_spec": "https://raw.githubusercontent.com/containerbuildsystem/atomic-reactor/master/atomic_reactor/schemas/content_manifest.json", 9 | "icm_version": 1, 10 | "image_layer_index": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pkg/scan/downloadCloser.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | // LayerDownloadReadCloser defines an image layer io.ReadCloser which downloads the layer(s) as needed. 10 | type LayerDownloadReadCloser struct { 11 | io.ReadCloser 12 | Downloader func() (io.ReadCloser, error) 13 | } 14 | 15 | // Read reads from the reader. 16 | func (l *LayerDownloadReadCloser) Read(p []byte) (int, error) { 17 | if l.ReadCloser == nil { 18 | readCloser, err := l.Downloader() 19 | if err != nil { 20 | return 0, errors.Wrap(err, "error downloading layer") 21 | } 22 | l.ReadCloser = readCloser 23 | } 24 | return l.ReadCloser.Read(p) 25 | } 26 | 27 | // Close closes the reader. 28 | func (l *LayerDownloadReadCloser) Close() error { 29 | if l.ReadCloser != nil { 30 | return l.ReadCloser.Close() 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /pkg/stringhelpers/contains.go: -------------------------------------------------------------------------------- 1 | package stringhelpers 2 | 3 | import "strings" 4 | 5 | // AnyContain checks the srcs to see if any contains the tgt and returns true if so 6 | func AnyContain(srcs []string, tgt string) bool { 7 | for _, s := range srcs { 8 | if strings.Contains(s, tgt) { 9 | return true 10 | } 11 | } 12 | return false 13 | } 14 | -------------------------------------------------------------------------------- /pkg/tarutil/testdata/symlink.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/tarutil/testdata/symlink.tar.gz -------------------------------------------------------------------------------- /pkg/tarutil/testdata/utils_test.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/tarutil/testdata/utils_test.tar -------------------------------------------------------------------------------- /pkg/tarutil/testdata/utils_test.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/tarutil/testdata/utils_test.tar.bz2 -------------------------------------------------------------------------------- /pkg/tarutil/testdata/utils_test.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/tarutil/testdata/utils_test.tar.gz -------------------------------------------------------------------------------- /pkg/tarutil/testdata/utils_test.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/tarutil/testdata/utils_test.tar.xz -------------------------------------------------------------------------------- /pkg/types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestConvertCVSSv3(t *testing.T) { 10 | type testcase struct { 11 | vector string 12 | baseScore float64 13 | impactScore float64 14 | exploitabilityScore float64 15 | } 16 | 17 | for _, c := range []testcase{ 18 | { 19 | vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H", 20 | baseScore: 8.3, 21 | impactScore: 6.0, 22 | exploitabilityScore: 1.6, 23 | }, 24 | { 25 | vector: "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H", 26 | baseScore: 7.5, 27 | impactScore: 6.0, 28 | exploitabilityScore: 0.8, 29 | }, 30 | { 31 | vector: "CVSS:3.0/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H", 32 | baseScore: 7.0, 33 | impactScore: 5.9, 34 | exploitabilityScore: 1.0, 35 | }, 36 | { 37 | vector: "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N", 38 | baseScore: 0.0, 39 | impactScore: 0.0, 40 | exploitabilityScore: 2.5, 41 | }, 42 | } { 43 | t.Run(c.vector, func(t *testing.T) { 44 | cvssv3, err := ConvertCVSSv3(c.vector) 45 | assert.NoError(t, err) 46 | assert.Equal(t, c.baseScore, cvssv3.Score) 47 | assert.Equal(t, c.impactScore, cvssv3.ImpactScore) 48 | assert.Equal(t, c.exploitabilityScore, cvssv3.ExploitabilityScore) 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pkg/updater/config.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Config defines the updater's configuration settings. 8 | // Any updates to this should be tested in cmd/clair/config_test.go. 9 | type Config struct { 10 | Interval time.Duration `yaml:"interval"` 11 | } 12 | -------------------------------------------------------------------------------- /pkg/updater/genesis_manifests_test.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "testing" 9 | 10 | "github.com/stackrox/rox/pkg/urlfmt" 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestValdiateUUID(t *testing.T) { 16 | _, filename, _, _ := runtime.Caller(0) 17 | 18 | // Test parsing testdata/fetcher_debian_test.json 19 | genesisFile, err := os.Open(filepath.Join(filepath.Dir(filename), "../../image/scanner/dump/genesis_manifests.json")) 20 | require.NoError(t, err) 21 | var manifest genesisManifest 22 | require.NoError(t, json.NewDecoder(genesisFile).Decode(&manifest)) 23 | 24 | for _, dump := range manifest.KnownGenesisDumps[1:] { 25 | assert.NoError(t, validateUUID(dump.UUID)) 26 | } 27 | 28 | assert.Error(t, validateUUID("invalid")) 29 | } 30 | 31 | func TestGetURL(t *testing.T) { 32 | centralEndpoint := "https://central.stackrox.svc" 33 | centralEndpoint = urlfmt.FormatURL(centralEndpoint, urlfmt.HTTPS, urlfmt.NoTrailingSlash) 34 | 35 | uuid := "55b9538d-0d42-4bbd-b4d4-5d31421e7302" 36 | url, err := getURL(centralEndpoint, uuid) 37 | assert.NoError(t, err) 38 | expected := "https://central.stackrox.svc/api/extensions/scannerdefinitions?uuid=55b9538d-0d42-4bbd-b4d4-5d31421e7302" 39 | assert.Equal(t, expected, url) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 clair authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | // Version is set at build time by the latest tag 18 | var Version string 19 | -------------------------------------------------------------------------------- /pkg/vulndump/loader_test.go: -------------------------------------------------------------------------------- 1 | package vulndump 2 | 3 | import ( 4 | "archive/zip" 5 | "os" 6 | "runtime" 7 | "testing" 8 | 9 | "github.com/stackrox/rox/pkg/utils" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func BenchmarkLoadOSVulnsFromDump(b *testing.B) { 14 | f := mustFetchOSVulns(b) 15 | defer utils.IgnoreError(f.Close) 16 | 17 | benchmarkLoadOSVulnsFromDump(b, f) 18 | } 19 | 20 | func BenchmarkLoadOSVulnsFromDump_Offline(b *testing.B) { 21 | f := mustFetchOfflineOSVulns(b) 22 | defer utils.IgnoreError(f.Close) 23 | 24 | benchmarkLoadOSVulnsFromDump(b, f) 25 | } 26 | 27 | func benchmarkLoadOSVulnsFromDump(b *testing.B, f *os.File) { 28 | zipR, err := zip.OpenReader(f.Name()) 29 | require.NoError(b, err) 30 | defer utils.IgnoreError(zipR.Close) 31 | 32 | runtime.GC() 33 | 34 | b.ResetTimer() 35 | for i := 0; i < b.N; i++ { 36 | vulns, err := LoadOSVulnsFromDump(&zipR.Reader) 37 | require.NoError(b, err) 38 | b.Logf("Loaded %d vulns", len(vulns)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/vulndump/well_known_names.go: -------------------------------------------------------------------------------- 1 | package vulndump 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/stackrox/scanner/database" 7 | ) 8 | 9 | // This block enumerates the files/directories in the vuln dump. 10 | // The vuln dump itself is a zip with all these directories. 11 | const ( 12 | ManifestFileName = "manifest.json" 13 | OSVulnsFileName = "os_vulns.json" 14 | RHELv2DirName = "rhelv2" 15 | RHELv2VulnsSubDirName = "vulns" 16 | NVDDirName = "nvd" 17 | RedHatDirName = "redhat" 18 | K8sDirName = "k8s" 19 | IstioDirName = "istio" 20 | ) 21 | 22 | // Manifest is used to JSON marshal/unmarshal the manifest.json file. 23 | type Manifest struct { 24 | Since time.Time `json:"since"` 25 | Until time.Time `json:"until"` 26 | } 27 | 28 | // RHELv2 is used to JSON marshal/unmarshal each RHELv2 JSON file. 29 | type RHELv2 struct { 30 | LastModified time.Time `json:"last_modified"` 31 | Vulns []*database.RHELv2Vulnerability `json:"vulns"` 32 | } 33 | -------------------------------------------------------------------------------- /pkg/vulnkey/key.go: -------------------------------------------------------------------------------- 1 | package vulnkey 2 | 3 | import "github.com/stackrox/scanner/database" 4 | 5 | // Key represents a unique identified for a vulnerability to be used as a key. 6 | type Key struct { 7 | name string 8 | namespace string 9 | } 10 | 11 | // FromVuln creates a Key from the given *database.Vulnerability. 12 | // It is expected the vulnerability is not nil and the namespace is not nil. 13 | func FromVuln(v *database.Vulnerability) Key { 14 | return Key{ 15 | name: v.Name, 16 | namespace: v.Namespace.Name, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/vulnloader/all/all.go: -------------------------------------------------------------------------------- 1 | package all 2 | 3 | import ( 4 | // Import all the vulnloader providers. 5 | _ "github.com/stackrox/scanner/pkg/vulnloader/istioloader" 6 | _ "github.com/stackrox/scanner/pkg/vulnloader/k8sloader" 7 | _ "github.com/stackrox/scanner/pkg/vulnloader/nvdloader" 8 | _ "github.com/stackrox/scanner/pkg/vulnloader/redhatloader" 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/vulnloader/driver.go: -------------------------------------------------------------------------------- 1 | package vulnloader 2 | 3 | var ( 4 | loaders = make(map[string]Loader) 5 | ) 6 | 7 | // Loader represents anything that can fetch vulnerabilities and store them in some directory. 8 | type Loader interface { 9 | // DownloadFeedsToPath downloads vulnerability feeds into the given path. 10 | DownloadFeedsToPath(string) error 11 | } 12 | 13 | // RegisterLoader makes a Loader available by the provided name. 14 | // 15 | // If called twice with the same name, the name is blank, or if the provided 16 | // Loader is nil, this function panics. 17 | // 18 | // Note: this function is not thread-safe, but should only be used in `init` functions. 19 | func RegisterLoader(name string, l Loader) { 20 | if name == "" { 21 | panic("vulnloader: could not register a Loader with an empty name") 22 | } 23 | 24 | if l == nil { 25 | panic("vulnloader: could not register nil Loader") 26 | } 27 | 28 | if _, dup := loaders[name]; dup { 29 | panic("vulnloader: RegisterLoader called twice for " + name) 30 | } 31 | 32 | loaders[name] = l 33 | } 34 | 35 | // Loaders returns the list of the registered Loaders. 36 | // 37 | // Note: this function is not thread-safe. 38 | func Loaders() map[string]Loader { 39 | return loaders 40 | } 41 | -------------------------------------------------------------------------------- /pkg/vulnloader/istioloader/loader.go: -------------------------------------------------------------------------------- 1 | package istioloader 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/go-git/go-git/v5" 8 | "github.com/pkg/errors" 9 | "github.com/stackrox/scanner/pkg/vulndump" 10 | "github.com/stackrox/scanner/pkg/vulnloader" 11 | ) 12 | 13 | const ( 14 | istioCVEsRepository = "https://github.com/stackrox/istio-cves.git" 15 | istioCVEsRefName = "refs/heads/main" 16 | ) 17 | 18 | func init() { 19 | vulnloader.RegisterLoader(vulndump.IstioDirName, &loader{}) 20 | } 21 | 22 | type loader struct{} 23 | 24 | func (l loader) DownloadFeedsToPath(outputDir string) error { 25 | tmpIstioDir := filepath.Join(outputDir, vulndump.IstioDirName+"-tmp") 26 | if err := os.MkdirAll(tmpIstioDir, 0755); err != nil { 27 | return errors.Wrapf(err, "creating subdir for %s", tmpIstioDir) 28 | } 29 | 30 | _, err := git.PlainClone(tmpIstioDir, false, &git.CloneOptions{ 31 | URL: istioCVEsRepository, 32 | ReferenceName: istioCVEsRefName, 33 | SingleBranch: true, 34 | }) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | return os.Rename(filepath.Join(tmpIstioDir, "vulns"), filepath.Join(outputDir, vulndump.IstioDirName)) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/vulnloader/istioloader/yaml.go: -------------------------------------------------------------------------------- 1 | package istioloader 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/ghodss/yaml" 7 | "github.com/pkg/errors" 8 | "github.com/stackrox/istio-cves/types" 9 | ) 10 | 11 | // LoadYAMLFileFromReader loads the Istio CVE feed from the given io.Reader. 12 | // It does NOT close the reader; that is the caller's responsibility. 13 | func LoadYAMLFileFromReader(r io.Reader) (types.Vuln, error) { 14 | contents, err := io.ReadAll(r) 15 | if err != nil { 16 | return types.Vuln{}, errors.Wrap(err, "reading YAML contents") 17 | } 18 | var vuln types.Vuln 19 | if err := yaml.Unmarshal(contents, &vuln); err != nil { 20 | return types.Vuln{}, errors.Wrap(err, "unmarshaling YAML from reader") 21 | } 22 | return vuln, nil 23 | } 24 | -------------------------------------------------------------------------------- /pkg/vulnloader/k8sloader/loader.go: -------------------------------------------------------------------------------- 1 | package k8sloader 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/go-git/go-git/v5" 8 | "github.com/pkg/errors" 9 | "github.com/stackrox/scanner/pkg/vulndump" 10 | "github.com/stackrox/scanner/pkg/vulnloader" 11 | ) 12 | 13 | const ( 14 | k8sCVEsRepository = "https://github.com/stackrox/k8s-cves.git" 15 | k8sCVEsRefName = "refs/heads/main" 16 | ) 17 | 18 | func init() { 19 | vulnloader.RegisterLoader(vulndump.K8sDirName, &loader{}) 20 | } 21 | 22 | type loader struct{} 23 | 24 | // DownloadFeedsToPath downloads the Kubernetes feeds to the given path. 25 | func (l *loader) DownloadFeedsToPath(outputDir string) error { 26 | tmpK8sDir := filepath.Join(outputDir, vulndump.K8sDirName+"-tmp") 27 | if err := os.MkdirAll(tmpK8sDir, 0755); err != nil { 28 | return errors.Wrapf(err, "creating subdir for %s", tmpK8sDir) 29 | } 30 | 31 | _, err := git.PlainClone(tmpK8sDir, false, &git.CloneOptions{ 32 | URL: k8sCVEsRepository, 33 | ReferenceName: k8sCVEsRefName, 34 | SingleBranch: true, 35 | }) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | return os.Rename(filepath.Join(tmpK8sDir, "cves"), filepath.Join(outputDir, vulndump.K8sDirName)) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/vulnloader/k8sloader/yaml.go: -------------------------------------------------------------------------------- 1 | package k8sloader 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/ghodss/yaml" 7 | "github.com/pkg/errors" 8 | "github.com/stackrox/k8s-cves/pkg/validation" 9 | ) 10 | 11 | // LoadYAMLFileFromReader loads the Kubernetes CVE feed from the given io.Reader. 12 | // It does NOT close the reader; that is the caller's responsibility. 13 | func LoadYAMLFileFromReader(r io.Reader) (*validation.CVESchema, error) { 14 | contents, err := io.ReadAll(r) 15 | if err != nil { 16 | return nil, errors.Wrap(err, "reading YAML contents") 17 | } 18 | var schema validation.CVESchema 19 | if err := yaml.Unmarshal(contents, &schema); err != nil { 20 | return nil, errors.Wrap(err, "unmarshaling YAML from reader") 21 | } 22 | return &schema, nil 23 | } 24 | -------------------------------------------------------------------------------- /pkg/wellknowndirnames/dir.go: -------------------------------------------------------------------------------- 1 | package wellknowndirnames 2 | 3 | const ( 4 | // WriteableDir is the root of all files that the scanner is allowed to write. 5 | // It is expected to be backed by an emptyDir. 6 | WriteableDir = "/var/lib/stackrox" 7 | ) 8 | -------------------------------------------------------------------------------- /pkg/wellknownkeys/list.go: -------------------------------------------------------------------------------- 1 | package wellknownkeys 2 | 3 | const ( 4 | // VulnUpdateTimestampKey is the key for the most recent vulnerability update. 5 | VulnUpdateTimestampKey = "vuln-update-ts-key" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/whiteout/pattern.go: -------------------------------------------------------------------------------- 1 | package whiteout 2 | 3 | var ( 4 | // Prefix prefix means file is a whiteout. If this is followed by a 5 | // filename this means that file has been removed from the base layer. 6 | Prefix = ".wh." 7 | // OpaqueDirectory means the directory does not exist in lower (parent) layers, 8 | // and may be ignored below the current layer. 9 | OpaqueDirectory = ".wh..wh..opq" 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/ziputil/open_test.go: -------------------------------------------------------------------------------- 1 | // This package exists to avoid import cycles. 2 | package ziputil_test 3 | 4 | import ( 5 | "archive/zip" 6 | "encoding/json" 7 | "path/filepath" 8 | "runtime" 9 | "testing" 10 | 11 | "github.com/stackrox/rox/pkg/utils" 12 | "github.com/stackrox/scanner/pkg/repo2cpe" 13 | "github.com/stackrox/scanner/pkg/vulndump" 14 | "github.com/stackrox/scanner/pkg/ziputil" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestOpenFile(t *testing.T) { 20 | _, filename, _, _ := runtime.Caller(0) 21 | testZip := filepath.Join(filepath.Dir(filename), "testdata/test.zip") 22 | 23 | zipR, err := zip.OpenReader(testZip) 24 | require.NoError(t, err) 25 | defer utils.IgnoreError(zipR.Close) 26 | 27 | m := repo2cpe.NewMapping() 28 | assert.NoError(t, m.LoadFromZip(&zipR.Reader, "rhelv2")) 29 | } 30 | 31 | func TestOpenFilesInDir(t *testing.T) { 32 | _, filename, _, _ := runtime.Caller(0) 33 | testZip := filepath.Join(filepath.Dir(filename), "testdata/test.zip") 34 | 35 | zipR, err := zip.OpenReader(testZip) 36 | require.NoError(t, err) 37 | defer utils.IgnoreError(zipR.Close) 38 | 39 | rcs, err := ziputil.OpenFilesInDir(&zipR.Reader, "rhelv2/vulns", ".json") 40 | assert.NoError(t, err) 41 | assert.Len(t, rcs, 1) 42 | 43 | var rhel vulndump.RHELv2 44 | assert.NoError(t, json.NewDecoder(rcs[0]).Decode(&rhel)) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/ziputil/testdata/test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackrox/scanner/6aca6bbf2869c1dd5fd23d9609c220f931d14066/pkg/ziputil/testdata/test.zip -------------------------------------------------------------------------------- /proto/scanner/api/v1/empty.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | option java_package = "io.stackrox.proto.api.scanner.v1"; 5 | 6 | package scannerV1; 7 | 8 | message Empty { 9 | } 10 | -------------------------------------------------------------------------------- /proto/scanner/api/v1/image.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | 5 | option java_package = "io.stackrox.proto.api.scanner.v1"; 6 | 7 | import "scanner/api/v1/vulnerability.proto"; 8 | 9 | package scannerV1; 10 | 11 | message Image { 12 | string namespace = 2; 13 | repeated Feature features = 1; 14 | } 15 | -------------------------------------------------------------------------------- /proto/scanner/api/v1/metadata_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | 5 | option java_package = "io.stackrox.proto.api.scanner.v1"; 6 | 7 | import weak "google/api/annotations.proto"; 8 | import "scanner/api/v1/empty.proto"; 9 | import "google/protobuf/timestamp.proto"; 10 | 11 | package scannerV1; 12 | 13 | message VulnDefsMetadata { 14 | google.protobuf.Timestamp last_updated_time = 1; 15 | } 16 | 17 | service VulnDefsService { 18 | rpc GetVulnDefsMetadata(Empty) returns (VulnDefsMetadata) { 19 | option (google.api.http) = { 20 | get: "/v1/vulndefs/metadata" 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /proto/scanner/api/v1/node_inventory_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | 5 | option java_package = "io.stackrox.proto.api.scanner.v1"; 6 | 7 | import "scanner/api/v1/note.proto"; 8 | import "scanner/api/v1/component.proto"; 9 | 10 | package scannerV1; 11 | 12 | message GetNodeInventoryResponse { 13 | string node_name = 1; 14 | Components components = 2; 15 | repeated Note notes = 3; 16 | } 17 | 18 | message GetNodeInventoryRequest {} 19 | 20 | // NodeInventoryService is used in Secured Clusters to fetch information from Nodes and communicate with other 21 | // Secured Cluster components, like the compliance container in Collector. 22 | service NodeInventoryService { 23 | rpc GetNodeInventory(GetNodeInventoryRequest) returns (GetNodeInventoryResponse) {} 24 | } 25 | -------------------------------------------------------------------------------- /proto/scanner/api/v1/note.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | 5 | option java_package = "io.stackrox.proto.api.scanner.v1"; 6 | 7 | package scannerV1; 8 | 9 | enum Note { 10 | OS_CVES_UNAVAILABLE = 0; 11 | OS_CVES_STALE = 1; 12 | LANGUAGE_CVES_UNAVAILABLE = 2; 13 | CERTIFIED_RHEL_SCAN_UNAVAILABLE = 3; 14 | } 15 | 16 | enum NodeNote { 17 | NODE_UNSUPPORTED = 0; 18 | NODE_KERNEL_UNSUPPORTED = 1; 19 | NODE_CERTIFIED_RHEL_CVES_UNAVAILABLE = 2; 20 | } 21 | -------------------------------------------------------------------------------- /proto/scanner/api/v1/ping_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "scannerV1"; 4 | option java_package = "io.stackrox.proto.api.scanner.v1"; 5 | 6 | import weak "google/api/annotations.proto"; 7 | import "scanner/api/v1/empty.proto"; 8 | 9 | package scannerV1; 10 | 11 | message PongMessage { 12 | string scanner_version = 2; 13 | string status = 1; 14 | } 15 | 16 | service PingService { 17 | rpc Ping(Empty) returns (PongMessage) { 18 | option (google.api.http) = { 19 | get: "/v1/ping" 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /rendered-chart/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /rpms.in.yaml: -------------------------------------------------------------------------------- 1 | # This rpms.in.yaml and other rpms.* files define how to make RPM packages available for Konflux image builds. 2 | # See our docs here: https://spaces.redhat.com/display/StackRox/How+to+prefetch+RPMs+for+ACS+Konflux+builds 3 | 4 | packages: 5 | - xz 6 | contentOrigin: 7 | repofiles: [ "rpms.rhel.repo" ] 8 | context: 9 | containerfile: 10 | file: image/scanner/rhel/konflux.Dockerfile 11 | stageName: scanner-common 12 | arches: 13 | - aarch64 14 | - ppc64le 15 | - s390x 16 | - x86_64 17 | -------------------------------------------------------------------------------- /rpms.rhel.repo: -------------------------------------------------------------------------------- 1 | [rhel-8-for-$basearch-baseos-rpms] 2 | name = Red Hat Enterprise Linux 8 for $basearch - BaseOS (RPMs) 3 | baseurl = https://cdn.redhat.com/content/dist/rhel8/8/$basearch/baseos/os 4 | enabled = 1 5 | gpgcheck = 1 6 | gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release 7 | sslverify = 1 8 | sslcacert = /etc/rhsm/ca/redhat-uep.pem 9 | sslclientkey = $SSL_CLIENT_KEY 10 | sslclientcert = $SSL_CLIENT_CERT 11 | sslverifystatus = 1 12 | metadata_expire = 86400 13 | enabled_metadata = 1 14 | 15 | [rhel-8-for-$basearch-baseos-source-rpms] 16 | name = Red Hat Enterprise Linux 8 for $basearch - BaseOS (Source RPMs) 17 | baseurl = https://cdn.redhat.com/content/dist/rhel8/8/$basearch/baseos/source/SRPMS 18 | enabled = 1 19 | gpgcheck = 1 20 | gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release 21 | sslverify = 1 22 | sslcacert = /etc/rhsm/ca/redhat-uep.pem 23 | sslclientkey = $SSL_CLIENT_KEY 24 | sslclientcert = $SSL_CLIENT_CERT 25 | sslverifystatus = 1 26 | metadata_expire = 86400 27 | enabled_metadata = 0 28 | -------------------------------------------------------------------------------- /scripts/cert/README.md: -------------------------------------------------------------------------------- 1 | # cert 2 | 3 | Scanner and ScannerDB requires valid certificates to run. 4 | `gen-cert.sh` generates certificates for these deployments 5 | and overwrites the current values in a given Kubernetes secret configuration file. 6 | 7 | This script requires [`cfssl`](https://github.com/cloudflare/cfssl), which 8 | may be installed via: 9 | ```sh 10 | go install github.com/cloudflare/cfssl/cmd/...@latest 11 | ``` 12 | 13 | To run from the top-level directory and overwrite `chart/templates/mock-scanner[-db]-tls.yaml`: 14 | ```sh 15 | ./scripts/cert/gen-cert.sh chart/templates/mock-scanner-tls.yaml chart/templates/mock-scanner-db-tls.yaml 16 | ``` 17 | -------------------------------------------------------------------------------- /scripts/cert/csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "algo": "ecdsa", 4 | "size": 256 5 | }, 6 | "names": [{ 7 | "C": "US", 8 | "L": "Raleigh", 9 | "O": "Red Hat, Inc.", 10 | "OU": "Engineering", 11 | "ST": "North Carolina" 12 | }] 13 | } 14 | -------------------------------------------------------------------------------- /scripts/ci/cleanup-deployment.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copied from https://github.com/stackrox/stackrox/blob/master/scripts/ci/cleanup-deployment.sh 4 | 5 | kubectl -n stackrox get cm,deploy,ds,networkpolicy,pv,pvc,secret,svc,serviceaccount -o name | xargs kubectl -n stackrox delete --wait 6 | -------------------------------------------------------------------------------- /scripts/ci/gcp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # A collection of GCP related reusable bash functions for CI 4 | # 5 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/gcp.sh 6 | 7 | SCRIPTS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 8 | # shellcheck source=../../scripts/ci/lib.sh 9 | source "$SCRIPTS_ROOT/scripts/ci/lib.sh" 10 | 11 | set -euo pipefail 12 | 13 | setup_gcp() { 14 | info "Setting up GCP auth and config" 15 | 16 | ensure_CI 17 | 18 | local service_account 19 | if [[ "$#" -gt 0 ]]; then 20 | service_account="$1" 21 | else 22 | require_environment "GOOGLE_SA_CIRCLECI_SCANNER" 23 | service_account="${GOOGLE_SA_CIRCLECI_SCANNER}" 24 | fi 25 | 26 | require_executable "gcloud" 27 | 28 | gcloud auth activate-service-account --key-file <(echo "$service_account") 29 | gcloud auth list 30 | gcloud config set project stackrox-ci 31 | gcloud config set compute/region us-central1 32 | gcloud config unset compute/zone 33 | gcloud config set core/disable_prompts True 34 | } 35 | -------------------------------------------------------------------------------- /scripts/ci/jobs/db-integration-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/jobs/go-postgres-tests.sh 4 | 5 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 6 | # shellcheck source=../../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | # shellcheck source=../../../scripts/ci/postgres.sh 9 | source "$ROOT/scripts/ci/postgres.sh" 10 | set -euo pipefail 11 | 12 | db_integration_tests() { 13 | info "Starting DB integration tests" 14 | 15 | start_postgres 16 | 17 | make db-integration-tests || touch FAIL 18 | 19 | if is_OPENSHIFT_CI; then 20 | info "Saving junit XML report" 21 | make generate-junit-reports || touch FAIL 22 | store_test_results junit-reports junit-reports 23 | fi 24 | 25 | [[ ! -f FAIL ]] || die "DB integration tests failed" 26 | } 27 | 28 | db_integration_tests "$*" 29 | -------------------------------------------------------------------------------- /scripts/ci/jobs/e2e_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S python3 -u 2 | 3 | """ 4 | Run E2E tests in a GKE cluster 5 | """ 6 | from pre_tests import PreE2ETests 7 | from ci_tests import E2ETest 8 | from post_tests import PostClusterTest, FinalPost 9 | from clusters import GKECluster 10 | from runners import ClusterTestRunner 11 | 12 | ClusterTestRunner( 13 | cluster=GKECluster("e2e-tests"), 14 | pre_test=PreE2ETests(), 15 | test=E2ETest(), 16 | post_test=PostClusterTest( 17 | check_stackrox_logs=True, 18 | artifact_destination_prefix="e2e-tests", 19 | ), 20 | final_post=FinalPost(), 21 | ).run() 22 | -------------------------------------------------------------------------------- /scripts/ci/jobs/e2etests/e2e-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../../.. && pwd)" 4 | # shellcheck source=../../../../scripts/ci/lib.sh 5 | source "$ROOT/scripts/ci/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | e2e_tests() { 10 | info "Starting e2e tests" 11 | 12 | make e2e-tests || touch FAIL 13 | 14 | info "Saving junit XML report" 15 | make generate-junit-reports || touch FAIL 16 | store_test_results junit-reports junit-reports 17 | 18 | [[ ! -f FAIL ]] || die "E2E tests failed" 19 | } 20 | 21 | e2e_tests "$*" 22 | -------------------------------------------------------------------------------- /scripts/ci/jobs/e2etests/scale-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../../.. && pwd)" 4 | # shellcheck source=../../../../scripts/ci/lib.sh 5 | source "$ROOT/scripts/ci/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | scale_tests() { 10 | info "Starting scale tests" 11 | 12 | make scale-tests 13 | } 14 | 15 | scale_tests "$*" 16 | -------------------------------------------------------------------------------- /scripts/ci/jobs/e2etests/slim-e2e-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../../.. && pwd)" 4 | # shellcheck source=../../../../scripts/ci/lib.sh 5 | source "$ROOT/scripts/ci/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | slim_e2e_tests() { 10 | info "Starting slim e2e tests" 11 | 12 | make slim-e2e-tests || touch FAIL 13 | 14 | info "Saving junit XML report" 15 | make generate-junit-reports || touch FAIL 16 | store_test_results junit-reports junit-reports 17 | 18 | [[ ! -f FAIL ]] || die "Slim E2E tests failed" 19 | } 20 | 21 | slim_e2e_tests "$*" 22 | -------------------------------------------------------------------------------- /scripts/ci/jobs/scale_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S python3 -u 2 | 3 | """ 4 | Run E2E tests in a GKE cluster 5 | """ 6 | from pre_tests import PreE2ETests 7 | from ci_tests import ScaleTest 8 | from post_tests import PostClusterTest, FinalPost 9 | from clusters import GKECluster 10 | from runners import ClusterTestRunner 11 | 12 | ClusterTestRunner( 13 | cluster=GKECluster("scale-tests"), 14 | pre_test=PreE2ETests(), 15 | test=ScaleTest(), 16 | post_test=PostClusterTest( 17 | check_stackrox_logs=True, 18 | artifact_destination_prefix="scale-tests", 19 | ), 20 | final_post=FinalPost(), 21 | ).run() 22 | -------------------------------------------------------------------------------- /scripts/ci/jobs/slim_e2e_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S python3 -u 2 | 3 | """ 4 | Run E2E tests in a GKE cluster 5 | """ 6 | from pre_tests import PreE2ETests 7 | from ci_tests import SlimE2ETest 8 | from post_tests import PostClusterTest, FinalPost 9 | from clusters import GKECluster 10 | from runners import ClusterTestRunner 11 | 12 | ClusterTestRunner( 13 | cluster=GKECluster("slim-e2e-tests"), 14 | pre_test=PreE2ETests(slim=True), 15 | test=SlimE2ETest(), 16 | post_test=PostClusterTest( 17 | check_stackrox_logs=True, 18 | artifact_destination_prefix="slim-e2e-tests", 19 | ), 20 | final_post=FinalPost(), 21 | ).run() 22 | -------------------------------------------------------------------------------- /scripts/ci/jobs/store-db-dump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/jobs/go-postgres-tests.sh 4 | 5 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 6 | # shellcheck source=../../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | set -euo pipefail 9 | 10 | store_db_dump() { 11 | info "Storing DB dump" 12 | store_test_results /tmp/postgres postgres 13 | } 14 | 15 | store_db_dump "$*" 16 | -------------------------------------------------------------------------------- /scripts/ci/jobs/store-genesis-dump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/jobs/go-postgres-tests.sh 4 | 5 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 6 | # shellcheck source=../../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | set -euo pipefail 9 | 10 | store_genesis_dump() { 11 | info "Storing genesis dump" 12 | store_test_results /tmp/genesis-dump genesis-dump 13 | } 14 | 15 | store_genesis_dump "$*" 16 | -------------------------------------------------------------------------------- /scripts/ci/jobs/style-checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 4 | # shellcheck source=../../../scripts/ci/lib.sh 5 | source "$ROOT/scripts/ci/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | style_checks() { 10 | info "Starting style checks" 11 | 12 | make style || touch FAIL 13 | 14 | if is_OPENSHIFT_CI; then 15 | info "Saving junit XML report" 16 | mkdir -p junit-reports 17 | cp -a report.xml "junit-reports/" || true 18 | store_test_results junit-reports reports 19 | fi 20 | 21 | [[ ! -f FAIL ]] || die "Style checks failed" 22 | } 23 | 24 | style_checks "$*" 25 | -------------------------------------------------------------------------------- /scripts/ci/jobs/unit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/jobs/go-unit-tests.sh 4 | 5 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 6 | # shellcheck source=../../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | 9 | set -euo pipefail 10 | 11 | unit_tests() { 12 | info "Starting unit tests" 13 | 14 | make unit-tests || touch FAIL 15 | 16 | if is_OPENSHIFT_CI; then 17 | info "Saving junit XML report" 18 | make generate-junit-reports || touch FAIL 19 | store_test_results junit-reports junit-reports 20 | fi 21 | 22 | [[ ! -f FAIL ]] || die "Unit tests failed" 23 | } 24 | 25 | unit_tests "$*" 26 | -------------------------------------------------------------------------------- /scripts/ci/jobs/upload-db-dump.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 4 | # shellcheck source=../../../scripts/ci/gcp.sh 5 | source "$ROOT/scripts/ci/gcp.sh" 6 | # shellcheck source=../../../scripts/ci/lib.sh 7 | source "$ROOT/scripts/ci/lib.sh" 8 | 9 | set -euo pipefail 10 | 11 | upload_db_dump() { 12 | if is_in_PR_context; then 13 | info "Skipping upload, as this is a PR" 14 | fi 15 | 16 | info "Starting DB dump upload" 17 | 18 | setup_gcp 19 | 20 | gsutil cp /tmp/postgres/pg-definitions.sql.gz gs://stackrox-scanner-ci-vuln-dump 21 | } 22 | 23 | upload_db_dump "$*" 24 | -------------------------------------------------------------------------------- /scripts/ci/jobs/upload-dumps-for-embedding.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../.. && pwd)" 4 | # shellcheck source=../../../scripts/ci/gcp.sh 5 | source "$ROOT/scripts/ci/gcp.sh" 6 | # shellcheck source=../../../scripts/lib.sh 7 | source "$ROOT/scripts/lib.sh" 8 | 9 | set -euo pipefail 10 | 11 | upload_dumps_for_embedding() { 12 | if is_in_PR_context; then 13 | info "In PR context. Skipping..." 14 | return 0 15 | fi 16 | 17 | info "Starting dumps upload" 18 | 19 | setup_gcp 20 | 21 | info "Uploading dumps" 22 | gsutil cp /tmp/vuln-dump/nvd-definitions.zip gs://stackrox-scanner-ci-vuln-dump 23 | gsutil cp /tmp/vuln-dump/k8s-definitions.zip gs://stackrox-scanner-ci-vuln-dump 24 | gsutil cp /tmp/vuln-dump/istio-definitions.zip gs://stackrox-scanner-ci-vuln-dump 25 | gsutil cp /tmp/vuln-dump/repo2cpe.zip gs://stackrox-scanner-ci-vuln-dump 26 | } 27 | 28 | upload_dumps_for_embedding "$*" 29 | -------------------------------------------------------------------------------- /scripts/ci/logcheck/allowlist-patterns: -------------------------------------------------------------------------------- 1 | # This file contains patterns that may be present in log files of any StackRox 2 | # service and that would otherwise trigger an error based on a 'blocklist' match. 3 | 4 | # postgres connections at startup messages are OK (ROX-4913) 5 | the database system is starting up 6 | # quay is intermittent 7 | unexpected status code 520 when retrieving image scan 8 | # scanner-db has gotten larger and the init container can trigger the autovacuum 9 | FATAL: terminating autovacuum process due to administrator command 10 | -------------------------------------------------------------------------------- /scripts/ci/logcheck/blocklist-patterns: -------------------------------------------------------------------------------- 1 | # This file contains patterns that must not be present in log files of any StackRox service, otherwise the 2 | # test will fail. 3 | # Patterns are in PCRE regex syntax, one per line. 4 | # Lines starting with a `#` as well as blank lines are ignored, but a `#` in the middle of a line is matched as-is. 5 | # If you want to match on a pattern starting with `#`, write `\#`. 6 | 7 | unexpected(?! EOF) 8 | panic 9 | fatal 10 | critical 11 | data race 12 | should (not|never) happen 13 | OMITTED.*LOG LINES DUE TO THROTTLING 14 | -------------------------------------------------------------------------------- /scripts/ci/logcheck/check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # check.sh 4 | # Checks if a file contains any pattern from a configurable blocklist. 5 | # Usage: check.sh 6 | # It returns a non-zero exit status if any offending patterns have been found. 7 | # 8 | # Copied from https://github.com/stackrox/stackrox/blob/master/scripts/ci/logcheck/check.sh 9 | 10 | DIR="$(cd "$(dirname "$0")" && pwd)" 11 | 12 | BLOCKLIST_FILE="${DIR}/blocklist-patterns" 13 | ALLOWLIST_FILE="${DIR}/allowlist-patterns" 14 | 15 | join_by() { local IFS="$1"; shift; echo "$*"; } 16 | 17 | IFS=$'\n' read -d '' -r -a blocklist_subpatterns < <(grep -E -v '^(#.*|\s*)$' "${BLOCKLIST_FILE}") 18 | 19 | blocklist_pattern="$(join_by '|' "${blocklist_subpatterns[@]}")" 20 | 21 | IFS=$'\n' read -d '' -r -a allowlist_subpatterns < <(grep -E -v '^(#.*|\s*)$' "${ALLOWLIST_FILE}") 22 | 23 | allowlist_pattern="$(join_by '|' "${allowlist_subpatterns[@]}")" 24 | 25 | grep -vP "$allowlist_pattern" "$@" | grep >&2 -Pni "$blocklist_pattern" && exit 1 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /scripts/ci/postgres.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Adapted from https://github.com/stackrox/stackrox/blob/master/scripts/ci/jobs/go-postgres-tests.sh 4 | 5 | SCRIPTS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 6 | # shellcheck source=../../scripts/lib.sh 7 | source "$SCRIPTS_ROOT/scripts/lib.sh" 8 | 9 | set -euo pipefail 10 | 11 | _start_postgres() { 12 | initdb "${HOME}/data" 13 | pg_ctl -D "${HOME}/data" -l logfile -o "-k /tmp" start 14 | export PGHOST=/tmp 15 | createuser -s postgres 16 | } 17 | 18 | start_postgres() { 19 | info "Starting Postgres" 20 | 21 | if [[ $(id -u) == 0 ]]; then 22 | info "This function should not be run as root." 23 | if is_CI; then 24 | info "Running in CI. Creating a non-root user." 25 | groupadd -g 1001 pg 26 | adduser pg -u 1001 -g 1001 -d /var/lib/postgresql -s /bin/sh 27 | 28 | # The PATH is not completely preserved, so set the PATH here to ensure postgres-related commands can be found. 29 | runuser -l pg -c "PATH=$PATH $SCRIPTS_ROOT/scripts/ci/postgres.sh _start_postgres" 30 | else 31 | die "Please re-run as a non-root user." 32 | fi 33 | else 34 | _start_postgres 35 | fi 36 | } 37 | 38 | if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then 39 | if [[ "$#" -lt 1 ]]; then 40 | die "When invoked at the command line a method is required." 41 | fi 42 | fn="$1" 43 | shift 44 | "$fn" "$@" 45 | fi 46 | -------------------------------------------------------------------------------- /scripts/ci/push-as-multiarch-manifest-list.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)" 4 | # shellcheck source=../../scripts/lib.sh 5 | source "$ROOT/scripts/lib.sh" 6 | 7 | set -euo pipefail 8 | 9 | [[ "$#" == 2 ]] || die "Usage: $0 " 10 | 11 | image="$1" 12 | IFS=',' read -ra architectures <<< "$2" 13 | 14 | [[ -n "$image" ]] || die "No image specified" 15 | [[ "$image" == *:* ]] || die "Must specify a tagged image reference when using this script" 16 | 17 | image_list=() 18 | for arch in "${architectures[@]}" 19 | do 20 | arch_image="${image}-${arch}" 21 | docker pull "${arch_image}" 22 | image_list+=("$arch_image") 23 | done 24 | 25 | docker manifest create "$image" "${image_list[@]}" 26 | 27 | # Try pushing manifest a few times for the case when quay.io has issues 28 | pushed=0 29 | for i in {1..5}; do 30 | echo "Pushing manifest for ${image}. Attempt ${i}..." 31 | echo docker manifest push "$image" 32 | if docker manifest push "$image"; then 33 | pushed=1 34 | break 35 | fi 36 | sleep 10 37 | done 38 | (( pushed )) 39 | -------------------------------------------------------------------------------- /scripts/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "Building with platform linux/${GOARCH}" 6 | if docker info | grep buildx; then 7 | docker buildx build --platform "linux/${GOARCH}" --load "$@" 8 | else 9 | docker build --platform "linux/${GOARCH}" "$@" 10 | fi 11 | -------------------------------------------------------------------------------- /scripts/generate-junit-reports.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copied from https://github.com/stackrox/stackrox/blob/master/scripts/generate-junit-reports.sh 4 | 5 | mkdir -p junit-reports 6 | 7 | go-junit-report <"test-output/test.log" >"junit-reports/report.xml" 8 | -------------------------------------------------------------------------------- /scripts/go-get-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function usage() { 4 | echo "To go get a package at a particular version, use:" 5 | echo "$0 [--skip-install]" 6 | exit 1 7 | } 8 | 9 | [[ "$#" -ge 2 ]] || usage 10 | 11 | export GO111MODULE=off # force legacy go get mode 12 | 13 | function install() { 14 | package="$1" 15 | hash_or_tag="$2" 16 | skip_install="$3" 17 | 18 | package_without_trailing_dots="${package%/...}" 19 | go get -d -v "${package_without_trailing_dots}" 20 | cd "${GOPATH}/src/${package_without_trailing_dots}" || { echo "Couldn't cd to the directory!"; return 1; } 21 | 22 | git rev-parse --git-dir &>/dev/null || { echo "This script only supports git-based packages!"; return 1; } 23 | git fetch --tags 24 | git checkout -q "${hash_or_tag}" || { echo "git checkout failed!"; exit 1; } 25 | [[ "$skip_install" = "--skip-install" ]] || go install "${package}" || { echo "go install failed!"; return 1; } 26 | } 27 | 28 | for i in {1..10}; do 29 | if install $@; then 30 | exit 0 31 | fi 32 | sleep 1 33 | done 34 | 35 | echo "failed all retries" 36 | exit 1 37 | -------------------------------------------------------------------------------- /scripts/node/dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/ubi8/ubi 2 | 3 | RUN yum -y install rpm-build rpm-libs rpm-build-libs rpm 4 | 5 | RUN mkdir tmp-specs 6 | 7 | COPY generate-rpm-specs.sh tmp-specs/generate-rpm-specs.sh 8 | -------------------------------------------------------------------------------- /scripts/node/generate-rpm-specs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is to generate simple RPM spec files: 4 | # spec => rpm files: run rpmbuild -bb *.spec 5 | # install rpm: yum localinstall /root/rpmbuild/RPMS/x86_64/*.rpm 6 | 7 | pkgcount=${1:?missing package count} 8 | for n in $(seq 1 $pkgcount); do 9 | cat >pkg-$(printf "%03d" ${n}).spec <&2 "Added missing newline at end of file: $file" 20 | echo >>"$file" 21 | return 0 22 | fi 23 | echo >&2 "Missing newline at end of file: $file" 24 | return 1 25 | fi 26 | } 27 | 28 | # Do nothing if no arguments given 29 | [[ "$#" -gt 0 ]] || exit 0 30 | 31 | if [[ "$1" == "--fix" ]]; then 32 | fix=1 33 | shift 34 | fi 35 | 36 | all_files=("$@") 37 | 38 | IFS=$'\n' read -d '' -r -a files < <( 39 | printf '%s\n' "${all_files[@]}" | grep -E "$EXT_REGEX" 40 | ) 41 | 42 | status=0 43 | for file in "${files[@]}"; do 44 | check_or_fix_newlines "$file" || status=1 45 | done 46 | 47 | exit "$status" 48 | -------------------------------------------------------------------------------- /tools/check-service-protos/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Flag service protos files which include "google/api/annotations.proto" but do not have their file names end with _service.proto 3 | all_protos=$(git ls-files *.proto) 4 | IFS=$'\n' read -d '' -r -a all_service_protos_without_service_in_name < <( 5 | git grep -l 'import .*"google/api/annotations.proto";' -- '*.proto' | grep -vE '_service\.proto$' 6 | ) 7 | 8 | [[ "${#all_service_protos_without_service_in_name[@]}" == 0 ]] || { 9 | echo "Found service proto files that do not end with _service.proto" 10 | echo "Files were: " 11 | printf " %s\n" ${all_service_protos_without_service_in_name[@]} 12 | exit 1 13 | } >&2 14 | -------------------------------------------------------------------------------- /tools/detect-large-files.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Finds large files that have been checked in to Git. 3 | set -euo pipefail 4 | 5 | GIT_REPO_TOP="$(git rev-parse --show-toplevel)" 6 | 7 | allowlist_path="" 8 | if [[ $# -eq 1 ]] 9 | then 10 | allowlist_path="$1" 11 | fi 12 | 13 | allowed_files="" 14 | if [[ -n "$allowlist_path" ]] 15 | then 16 | LC_ALL=C LC_COLLATE=C sort --check "${allowlist_path}" 17 | allowed_files=$(egrep -v '^\s*(#.*)$' "${allowlist_path}" | xargs git -C "$GIT_REPO_TOP" ls-files --) 18 | fi 19 | 20 | large_files=$(git ls-tree --full-tree -l -r HEAD "$GIT_REPO_TOP" | awk '$4 > 50*1024 {print$5}') 21 | 22 | 23 | IFS=$'\n' read -d '' -r -a non_allowed_files < <( 24 | { 25 | echo "${large_files}" 26 | echo "${allowed_files}" 27 | echo "${allowed_files}" 28 | } | sort | uniq -u 29 | ) || true 30 | 31 | 32 | [[ "${#non_allowed_files[@]}" == 0 ]] || { 33 | echo "Found large files in the working tree. Please remove them!" 34 | echo "If you must add them, you need to explicitly add them to the allow list file ${allowlist_path:-and provide it as an argument to this script.}" 35 | echo "Files were: " 36 | printf ' %s\n' "${non_allowed_files[@]}" 37 | exit 1 38 | } >&2 39 | -------------------------------------------------------------------------------- /tools/fix-blanks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | import_validate="$(dirname "${BASH_SOURCE[0]}")/import_validate.py" 4 | 5 | # The following awk program parses lines (of the form `::&2 "Malformed awk output line $line ..." 29 | exit 1 30 | fi 31 | deletions_only="${sed_cmd//[^d]/}" 32 | echo >&2 "Deleting ${#deletions_only} blank line(s) from $file" 33 | sed -i'.bak' -e "$sed_cmd" "$file" 34 | rm "${file}.bak" 35 | done < <(awk -F: "$awk_prog" <("$import_validate" "$@") ) 36 | -------------------------------------------------------------------------------- /tools/linters/tools-import.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | // This file declares dependencies on tool for `go mod` purposes. 7 | // See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 8 | // for an explanation of the approach. 9 | 10 | import ( 11 | // Tool dependencies, not used anywhere in the code. 12 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 13 | _ "honnef.co/go/tools/cmd/staticcheck" 14 | ) 15 | -------------------------------------------------------------------------------- /tools/linters/tools.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | // This file only exists to prevent package loading errors for this directory. 4 | -------------------------------------------------------------------------------- /tools/local-nodescanner/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_REGISTRY=registry.access.redhat.com 2 | ARG BASE_IMAGE=ubi9-minimal 3 | ARG BASE_TAG=9.1 4 | 5 | FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} AS base 6 | 7 | COPY ./bin/local-nodescanner /local-nodescanner 8 | 9 | ENTRYPOINT [ "/local-nodescanner" ] 10 | -------------------------------------------------------------------------------- /tools/test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stackrox/scanner/tools/test 2 | 3 | go 1.23 4 | 5 | toolchain go1.23.6 6 | 7 | require github.com/jstemmer/go-junit-report/v2 v2.1.0 8 | -------------------------------------------------------------------------------- /tools/test/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 2 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc= 4 | github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ= 5 | -------------------------------------------------------------------------------- /tools/test/tools-import.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | // This file declares dependencies on tool for `go mod` purposes. 7 | // See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 8 | // for an explanation of the approach. 9 | 10 | import ( 11 | // Tool dependencies, not used anywhere in the code. 12 | _ "github.com/jstemmer/go-junit-report/v2" 13 | ) 14 | -------------------------------------------------------------------------------- /tools/tools-import.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | // This file declares dependencies on tool for `go mod` purposes. 7 | // See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 8 | // for an explanation of the approach. 9 | 10 | import ( 11 | // Tool dependencies, not used anywhere in the code. 12 | _ "github.com/ckaznocha/protoc-gen-lint" 13 | _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" 14 | _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" 15 | _ "github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto" 16 | _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" 17 | ) 18 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | // This file only exists to prevent package loading errors for this directory. 4 | --------------------------------------------------------------------------------