1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
|
INCLUDE(generate_submodule_info)
INCLUDE(ExternalProject)
# Extract user name and repository name from a github URL.
FUNCTION (EXTRACT_REPO_NAME_AND_USER repo_url repo_name_var repo_user_var)
IF(repo_url MATCHES "^git@")
# normalize to https-style URLs
STRING(REGEX REPLACE "^git@([^:]+):(.*)$" "https://\\1/\\2" repo_url "${repo_url}")
ENDIF()
# Extract the repository user
STRING(REGEX REPLACE "https://([^/]+)/([^/]+)/.*" "\\2" repo_user "${repo_url}")
STRING(REGEX REPLACE ".*/([^/]*)$" "\\1" repo_name "${repo_url}")
STRING(REGEX REPLACE "\\.git$" "" repo_name "${repo_name}")
SET(${repo_name_var} ${repo_name} PARENT_SCOPE)
SET(${repo_user_var} ${repo_user} PARENT_SCOPE)
ENDFUNCTION()
# Add a known 3rd party dependency for SBOM generation
# Currently used for "vendored" (part of our repository) source code we know about
# such as zlib, as well ExternalProject_Add() projects
MACRO(ADD_THIRD_PARTY_DEPENDENCY name url tag rev version description)
LIST(FIND ALL_THIRD_PARTY ${name} idx)
IF (idx GREATER -1)
MESSAGE(FATAL_ERROR "${name} is already in ALL_THIRD_PARTY")
ENDIF()
SET(${name}_URL ${url})
SET(${name}_TAG ${tag})
SET(${name}_REVISION ${rev})
SET(${name}_DESCRIPTION "${description}")
SET(${name}_VERSION "${version}")
LIST(APPEND ALL_THIRD_PARTY ${name})
ENDMACRO()
# Get CPE ID ( https://en.wikipedia.org/wiki/Common_Platform_Enumeration )
# for given project name and version
# CPE prefix are stored with other auxilliary info in the 3rdparty_info.cmake
# file
FUNCTION(SBOM_GET_CPE name version var)
SET(${var} "" PARENT_SCOPE)
STRING(FIND "${version}" "." dot_idx)
IF(${dot_idx} EQUAL -1)
# Version does not have dot inside.
# mostly likely it is just a git hash
RETURN()
ENDIF()
SET(cpe_name_and_vendor "${${repo_name_lower}.cpe-prefix}")
IF(NOT cpe_name_and_vendor)
RETURN()
ENDIF()
STRING(REGEX REPLACE "[^0-9\\.]" "" cleaned_version "${version}")
SET(${var} "cpe:2.3:a:${cpe_name_and_vendor}:${cleaned_version}:*:*:*:*:*:*:*" PARENT_SCOPE)
ENDFUNCTION()
# Add dependency on CMake ExternalProject.
# Currently, only works for github hosted projects,
# URL property of the external project needs to point to release source download
MACRO(ADD_CMAKE_EXTERNAL_PROJECT_DEPENDENCY name)
ExternalProject_GET_PROPERTY(${name} URL)
STRING(REGEX REPLACE "https://github.com/([^/]+/[^/]+)/releases/download/([^/]+)/.*-([^-]+)\\..*" "\\1;\\2;\\3" parsed "${URL}")
# Split the result into components
LIST(LENGTH parsed parsed_length)
IF(parsed_length EQUAL 3)
LIST(GET parsed 0 project_path)
LIST(GET parsed 1 tag)
LIST(GET parsed 2 ver)
ELSE()
STRING(REGEX REPLACE "https://github.com/([^/]+/[^/]+)/archive/refs/tags/([^/]+)\\.(tar\\.gz|zip)$" "\\1;\\2;\\3" parsed "${URL}")
LIST(LENGTH parsed parsed_length)
IF(parsed_length GREATER 1)
LIST(GET parsed 0 project_path)
LIST(GET parsed 1 tag)
STRING(REGEX REPLACE "[^0-9.]" "" ver "${tag}")
ELSE()
MESSAGE(FATAL_ERROR "Unexpected format for the download URL of project ${name} : (${URL})")
ENDIF()
ENDIF()
ADD_THIRD_PARTY_DEPENDENCY(${name} "https://github.com/${project_path}" "${tag}" "${tag}" "${ver}" "")
ENDMACRO()
# Match third party component with supplier
# CyclonDX documentation says it is
# "The organization that supplied the component.
# The supplier may often be the manufacturer, but may also be a distributor or repackager."
#
# Perhaps it can always be "MariaDB", but security team recommendation is different
# more towards "author"
FUNCTION (sbom_get_supplier repo_name repo_user varname)
IF("${${repo_name}_SUPPLIER}")
SET(${varname} "${${repo_name}_SUPPLIER}" PARENT_SCOPE)
ELSEIF (repo_name MATCHES "zlib|minizip")
# stuff that is checked into out repos
SET(${varname} "MariaDB" PARENT_SCOPE)
ELSEIF (repo_name MATCHES "boost")
SET(${varname} "Boost.org" PARENT_SCOPE)
ELSEIF(repo_user MATCHES "mariadb-corporation|mariadb")
SET(${varname} "MariaDB")
ELSE()
# Capitalize just first letter in repo_user
STRING(SUBSTRING "${repo_user}" 0 1 first_letter)
STRING(SUBSTRING "${repo_user}" 1 -1 rest)
STRING(TOUPPER "${first_letter}" first_letter_upper)
SET(${varname} "${first_letter_upper}${rest}" PARENT_SCOPE)
ENDIF()
ENDFUNCTION()
# Generate sbom.json in the top-level build directory
FUNCTION(GENERATE_SBOM)
IF(EXISTS ${PROJECT_SOURCE_DIR}/cmake/submodule_info.cmake)
INCLUDE(${PROJECT_SOURCE_DIR}/cmake/submodule_info.cmake)
ELSE()
GENERATE_SUBMODULE_INFO(${PROJECT_BINARY_DIR}/cmake/submodule_info.cmake)
INCLUDE(${PROJECT_BINARY_DIR}/cmake/submodule_info.cmake)
ENDIF()
# Remove irrelevant for the current build submodule information
# That is, if we do not build say columnstore, do not include
# dependency info into SBOM
IF(NOT TARGET wolfssl)
# using openssl, rather than wolfssl
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX wolfssl)
ENDIF()
IF(NOT WITH_WSREP)
# wsrep is not compiled
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX wsrep)
ENDIF()
IF(NOT TARGET columnstore)
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX columnstore)
ENDIF()
IF(NOT TARGET rocksdb)
# Rocksdb is not compiled
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX rocksdb)
ENDIF()
IF(NOT TARGET s3)
# S3 aria is not compiled
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX storage/maria/libmarias3)
ENDIF()
# libmariadb/docs is not a library, so remove it
LIST(FILTER ALL_SUBMODULES EXCLUDE REGEX libmariadb/docs)
# It is possible to provide EXTRA_SBOM_DEPENDENCIES
# and accompanying per-dependency data, to extend generared sbom
# document.
# Example below injects an extra "ncurses" dependency using several
# command line parameters for CMake.
# -DEXTRA_SBOM_DEPENDENCIES=ncurses
# -Dncurses_URL=https://github.com/mirror/ncurses
# -Dncurses_TAG=v6.4
# -Dncurses_VERSION=6.4
# -Dncurses_DESCRIPTION="A fake extra dependency"
SET(ALL_THIRD_PARTY ${ALL_SUBMODULES} ${EXTRA_SBOM_DEPENDENCIES})
# Add dependencies on cmake ExternalProjects
FOREACH(ext_proj libfmt pcre2)
IF(TARGET ${ext_proj})
ADD_CMAKE_EXTERNAL_PROJECT_DEPENDENCY(${ext_proj})
ENDIF()
ENDFOREACH()
# ZLIB
IF(TARGET zlib OR TARGET connect)
# Path to the zlib.h file
SET(ZLIB_HEADER_PATH "${PROJECT_SOURCE_DIR}/zlib/zlib.h")
# Variable to store the extracted version
SET(ZLIB_VERSION "")
# Read the version string from the file
file(STRINGS "${ZLIB_HEADER_PATH}" ZLIB_VERSION_LINE REGEX "#define ZLIB_VERSION.*")
# Extract the version number using a regex
IF (ZLIB_VERSION_LINE)
STRING(REGEX MATCH "\"([^\"]+)\"" ZLIB_VERSION_MATCH "${ZLIB_VERSION_LINE}")
IF (ZLIB_VERSION_MATCH)
STRING(REPLACE "\"" "" ZLIB_VERSION "${ZLIB_VERSION_MATCH}")
IF(NOT ("${ZLIB_VERSION}" MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+"))
MESSAGE(FATAL_ERROR "Unexpected zlib version '${ZLIB_VERSION}' parsed from ${ZLIB_HEADER_PATH}")
ENDIF()
ELSE()
MESSAGE(FATAL_ERROR "Could not extract ZLIB version from the line: ${ZLIB_VERSION_LINE}")
ENDIF()
ELSE()
MESSAGE(FATAL_ERROR "ZLIB_VERSION definition not found in ${ZLIB_HEADER_PATH}")
ENDIF()
IF(TARGET zlib)
ADD_THIRD_PARTY_DEPENDENCY(zlib "https://github.com/madler/zlib"
"v${ZLIB_VERSION}" "v${ZLIB_VERSION}" "${ZLIB_VERSION}" "Vendored zlib included into server source")
ENDIF()
IF(TARGET ha_connect OR TARGET connect)
SET(minizip_PURL "pkg:github/madler/zlib@${ZLIB_VERSION}?path=contrib/minizip")
ADD_THIRD_PARTY_DEPENDENCY(minizip "https://github.com/madler/zlib?path=contrib/minizip"
"v${ZLIB_VERSION}-minizip" "v${ZLIB_VERSION}-minizip" "${ZLIB_VERSION}"
"Vendored minizip (zip.c, unzip.c, ioapi.c) in connect engine, copied from zlib/contributions")
ENDIF()
ENDIF()
IF(TARGET columnstore)
# Determining if Columnstore builds Boost is tricky.
# The presence of the external_boost target isn't reliable, it is always
# present. Instead, we check indirectly by verifying if one of the libraries
# built by the external project exists in the build directory.
IF(TARGET external_boost AND TARGET boost_filesystem)
GET_TARGET_PROPERTY(boost_filesystem_loc boost_filesystem IMPORTED_LOCATION)
STRING(FIND "${boost_filesystem_loc}" "${CMAKE_BINARY_DIR}" idx)
IF(idx EQUAL 0)
# Now we can be reasonably sure, external_boost is indeed an external project
ExternalProject_GET_PROPERTY(external_boost URL)
# Extract the version from the URL using string manipulation.
STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" BOOST_VERSION ${URL})
SET(tag boost-${BOOST_VERSION})
ADD_THIRD_PARTY_DEPENDENCY(boost
"https://github.com/boostorg/boost" "${tag}" "${tag}" "${BOOST_VERSION}"
"Boost library, linked with columnstore engine")
ENDIF()
ENDIF()
IF(TARGET external_thrift)
ADD_CMAKE_EXTERNAL_PROJECT_DEPENDENCY(external_thrift)
ENDIF()
ENDIF()
SET(sbom_components "")
SET(sbom_dependencies "\n {
\"ref\": \"${CPACK_PACKAGE_NAME}\",
\"dependsOn\": [" )
INCLUDE(3rdparty_info)
SET(first ON)
FOREACH(dep ${ALL_THIRD_PARTY})
# Extract the part after the last "/" from URL
SET(revision ${${dep}_REVISION})
SET(tag ${${dep}_TAG})
SET(desc ${${dep}_DESCRIPTION})
IF((tag STREQUAL "no-tag") OR (NOT tag))
SET(tag ${revision})
ENDIF()
IF (NOT "${revision}" AND "${tag}")
SET(revision ${tag})
ENDIF()
SET(version ${${dep}_VERSION})
IF (version)
ELSEIF(tag)
SET(version ${tag})
ELSEIF(revision)
SET(version ${revision})
ENDIF()
EXTRACT_REPO_NAME_AND_USER("${${dep}_URL}" repo_name repo_user)
IF(first)
SET(first OFF)
ELSE()
STRING(APPEND sbom_components ",")
STRING(APPEND sbom_dependencies ",")
ENDIF()
SET(bom_ref "${repo_name}-${version}")
IF(desc)
SET(desc_line "\n \"description\": \"${desc}\",")
ELSE()
SET(desc_line "")
ENDIF()
STRING(TOLOWER "${repo_user}" repo_user_lower)
STRING(TOLOWER "${repo_name}" repo_name_lower)
IF (${repo_name_lower}_PURL)
SET(purl "${${repo_name_lower}_PURL}")
ELSE()
SET(purl "pkg:github/${repo_user_lower}/${repo_name_lower}@${revision}")
ENDIF()
SBOM_GET_SUPPLIER(${repo_name_lower} ${repo_user_lower} supplier)
SBOM_GET_CPE(${repo_name_lower} "${version}" cpe)
IF(cpe)
SET(cpe "\n \"cpe\": \"${cpe}\",")
ENDIF()
SET(license "${${repo_name_lower}.license}")
IF(NOT license)
MESSAGE(FATAL_ERROR "no license for 3rd party dependency ${repo_name_lower}.")
ENDIF()
SET(copyright "${${repo_name_lower}.copyright}")
IF(NOT copyright)
SET(copyright NOASSERTION)
ENDIF()
STRING(APPEND sbom_components "
{
\"bom-ref\": \"${bom_ref}\",
\"type\": \"library\",
\"name\": \"${repo_name}\",
\"version\": \"${version}\",${desc_line}
\"purl\": \"${purl}\",${cpe}
\"supplier\": {
\"name\": \"${supplier}\"
},
\"licenses\": [
{
\"license\": {
\"id\": \"${license}\"
}
}
],
\"copyright\": \"${copyright}\"
}")
STRING(APPEND sbom_dependencies "
\"${bom_ref}\"")
STRING(APPEND reflist ",\n {\"ref\": \"${bom_ref}\"}")
ENDFOREACH()
STRING(APPEND sbom_dependencies "\n ]\n }${reflist}\n")
STRING(UUID UUID NAMESPACE ee390ca3-e70f-4b35-808e-a512489156f5 NAME SBOM TYPE SHA1)
STRING(TIMESTAMP TIMESTAMP "%Y-%m-%dT%H:%M:%SZ" UTC)
EXTRACT_REPO_NAME_AND_USER("${GIT_REMOTE_ORIGIN_URL}" GITHUB_REPO_NAME GITHUB_REPO_USER)
#github-purl needs lowercased user and project names
STRING(TOLOWER "${GITHUB_REPO_NAME}" GITHUB_REPO_NAME)
STRING(TOLOWER "${GITHUB_REPO_USER}" GITHUB_REPO_USER)
IF(NOT DEFINED CPACK_PACKAGE_VERSION)
SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
ENDIF()
STRING(TIMESTAMP CURRENT_YEAR "%Y")
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/sbom.json.in ${CMAKE_BINARY_DIR}/sbom.json)
ENDFUNCTION()
|