feat: add generated SPDX file on bottling
This commit is contained in:
		
							parent
							
								
									802487d6f0
								
							
						
					
					
						commit
						a43b7464c2
					
				@ -35,7 +35,7 @@ end
 | 
			
		||||
group :man, optional: true do
 | 
			
		||||
  gem "kramdown", require: false
 | 
			
		||||
end
 | 
			
		||||
group :pr_upload, optional: true do
 | 
			
		||||
group :pr_upload, :bottle, optional: true do
 | 
			
		||||
  gem "json_schemer", require: false
 | 
			
		||||
end
 | 
			
		||||
group :prof, optional: true do
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ require "fileutils"
 | 
			
		||||
require "formula"
 | 
			
		||||
require "utils/bottles"
 | 
			
		||||
require "tab"
 | 
			
		||||
require "sbom"
 | 
			
		||||
require "keg"
 | 
			
		||||
require "formula_versions"
 | 
			
		||||
require "utils/inreplace"
 | 
			
		||||
@ -95,6 +96,8 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      sig { override.void }
 | 
			
		||||
      def run
 | 
			
		||||
        Homebrew.install_bundler_gems!(groups: ["bottle"])
 | 
			
		||||
 | 
			
		||||
        if args.merge?
 | 
			
		||||
          Homebrew.install_bundler_gems!(groups: ["ast"])
 | 
			
		||||
          return merge
 | 
			
		||||
@ -491,6 +494,8 @@ module Homebrew
 | 
			
		||||
            Tab.clear_cache
 | 
			
		||||
            Dependency.clear_cache
 | 
			
		||||
            Requirement.clear_cache
 | 
			
		||||
            SBOM.clear_cache
 | 
			
		||||
 | 
			
		||||
            tab = keg.tab
 | 
			
		||||
            original_tab = tab.dup
 | 
			
		||||
            tab.poured_from_bottle = false
 | 
			
		||||
@ -503,6 +508,9 @@ module Homebrew
 | 
			
		||||
              tab.write
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            sbom = SBOM.create(formula)
 | 
			
		||||
            sbom.write
 | 
			
		||||
 | 
			
		||||
            keg.consistent_reproducible_symlink_permissions!
 | 
			
		||||
 | 
			
		||||
            cd cellar do
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										377
									
								
								Library/Homebrew/sbom.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								Library/Homebrew/sbom.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,377 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "cxxstdlib"
 | 
			
		||||
require "json"
 | 
			
		||||
require "development_tools"
 | 
			
		||||
require "extend/cachable"
 | 
			
		||||
require "utils/curl"
 | 
			
		||||
 | 
			
		||||
# Rather than calling `new` directly, use one of the class methods like {SBOM.create}.
 | 
			
		||||
class SBOM
 | 
			
		||||
  extend Cachable
 | 
			
		||||
 | 
			
		||||
  FILENAME = "sbom.spdx.json"
 | 
			
		||||
  SCHEMA = "https://raw.githubusercontent.com/spdx/spdx-spec/v2.3/schemas/spdx-schema.json"
 | 
			
		||||
 | 
			
		||||
  attr_accessor :homebrew_version, :spdxfile, :built_as_bottle, :installed_as_dependency, :installed_on_request,
 | 
			
		||||
                :changed_files, :poured_from_bottle, :loaded_from_api, :time, :stdlib, :aliases, :arch, :source,
 | 
			
		||||
                :built_on, :license, :name
 | 
			
		||||
  attr_writer :compiler, :runtime_dependencies, :source_modified_time
 | 
			
		||||
 | 
			
		||||
  # Instantiates a {SBOM} for a new installation of a formula.
 | 
			
		||||
  sig { params(formula: Formula, compiler: T.nilable(String), stdlib: T.nilable(String)).returns(T.attached_class) }
 | 
			
		||||
  def self.create(formula, compiler: nil, stdlib: nil)
 | 
			
		||||
    runtime_deps = formula.runtime_formula_dependencies(undeclared: false)
 | 
			
		||||
 | 
			
		||||
    attributes = {
 | 
			
		||||
      name:                    formula.name,
 | 
			
		||||
      homebrew_version:        HOMEBREW_VERSION,
 | 
			
		||||
      spdxfile:                formula.prefix/FILENAME,
 | 
			
		||||
      built_as_bottle:         formula.build.bottle?,
 | 
			
		||||
      installed_as_dependency: false,
 | 
			
		||||
      installed_on_request:    false,
 | 
			
		||||
      poured_from_bottle:      false,
 | 
			
		||||
      loaded_from_api:         false,
 | 
			
		||||
      time:                    Time.now.to_i,
 | 
			
		||||
      source_modified_time:    formula.source_modified_time.to_i,
 | 
			
		||||
      compiler:,
 | 
			
		||||
      stdlib:,
 | 
			
		||||
      aliases:                 formula.aliases,
 | 
			
		||||
      runtime_dependencies:    SBOM.runtime_deps_hash(runtime_deps),
 | 
			
		||||
      arch:                    Hardware::CPU.arch,
 | 
			
		||||
      license:                 SPDX.license_expression_to_string(formula.license),
 | 
			
		||||
      built_on:                DevelopmentTools.build_system_info,
 | 
			
		||||
      source:                  {
 | 
			
		||||
        path:         formula.specified_path.to_s,
 | 
			
		||||
        tap:          formula.tap&.name,
 | 
			
		||||
        tap_git_head: nil, # Filled in later if possible
 | 
			
		||||
        spec:         formula.active_spec_sym.to_s,
 | 
			
		||||
        patches:      formula.stable&.patches,
 | 
			
		||||
        bottle:       formula.bottle_hash,
 | 
			
		||||
        stable:       {
 | 
			
		||||
          version:  formula.stable&.version,
 | 
			
		||||
          url:      formula.stable&.url,
 | 
			
		||||
          checksum: formula.stable&.checksum,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # We can only get `tap_git_head` if the tap is installed locally
 | 
			
		||||
    attributes[:source][:tap_git_head] = T.must(formula.tap).git_head if formula.tap&.installed?
 | 
			
		||||
 | 
			
		||||
    new(attributes)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(attributes: Hash).void }
 | 
			
		||||
  def initialize(attributes = {})
 | 
			
		||||
    attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def valid?
 | 
			
		||||
    data = to_spdx_sbom
 | 
			
		||||
 | 
			
		||||
    schema_string, _, status = Utils::Curl.curl_output(SCHEMA)
 | 
			
		||||
 | 
			
		||||
    opoo "Failed to fetch schema!" unless status.success?
 | 
			
		||||
 | 
			
		||||
    require "json_schemer"
 | 
			
		||||
 | 
			
		||||
    schemer = JSONSchemer.schema(schema_string)
 | 
			
		||||
 | 
			
		||||
    return true if schemer.valid?(data)
 | 
			
		||||
 | 
			
		||||
    opoo "SBOM validation errors:"
 | 
			
		||||
    schemer.validate(data).to_a.each do |error|
 | 
			
		||||
      ohai error["error"]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    odie "Failed to validate SBOM agains schema!" if ENV["HOMEBREW_ENFORCE_SBOM"]
 | 
			
		||||
 | 
			
		||||
    false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { void }
 | 
			
		||||
  def write
 | 
			
		||||
    # If this is a new installation, the cache of installed formulae
 | 
			
		||||
    # will no longer be valid.
 | 
			
		||||
    Formula.clear_cache unless spdxfile.exist?
 | 
			
		||||
 | 
			
		||||
    self.class.cache[spdxfile] = self
 | 
			
		||||
 | 
			
		||||
    unless valid?
 | 
			
		||||
      opoo "SBOM is not valid, not writing to disk!"
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    spdxfile.atomic_write(JSON.pretty_generate(to_spdx_sbom))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(runtime_dependency_declaration: T::Array[Hash], compiler_declaration: Hash).returns(T::Array[Hash]) }
 | 
			
		||||
  def generate_relations_json(runtime_dependency_declaration, compiler_declaration)
 | 
			
		||||
    runtime = runtime_dependency_declaration.map do |dependency|
 | 
			
		||||
      {
 | 
			
		||||
        spdxElementId:      dependency[:SPDXID],
 | 
			
		||||
        relationshipType:   "RUNTIME_DEPENDENCY_OF",
 | 
			
		||||
        relatedSpdxElement: "SPDXRef-Bottle-#{name}",
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
    patches = source[:patches].each_with_index.map do |_patch, index|
 | 
			
		||||
      {
 | 
			
		||||
        spdxElementId:      "SPDXRef-Patch-#{name}-#{index}",
 | 
			
		||||
        relationshipType:   "PATCH_APPLIED",
 | 
			
		||||
        relatedSpdxElement: "SPDXRef-Archive-#{name}-src",
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    base = [
 | 
			
		||||
      {
 | 
			
		||||
        spdxElementId:      "SPDXRef-File-#{name}",
 | 
			
		||||
        relationshipType:   "PACKAGE_OF",
 | 
			
		||||
        relatedSpdxElement: "SPDXRef-Archive-#{name}-src",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        spdxElementId:      "SPDXRef-Compiler",
 | 
			
		||||
        relationshipType:   "BUILD_TOOL_OF",
 | 
			
		||||
        relatedSpdxElement: "SPDXRef-Package-#{name}-src",
 | 
			
		||||
      },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    if compiler_declaration["SPDXRef-Stdlib"].present?
 | 
			
		||||
      base += {
 | 
			
		||||
        spdxElementId:      "SPDXRef-Stdlib",
 | 
			
		||||
        relationshipType:   "DEPENDENCY_OF",
 | 
			
		||||
        relatedSpdxElement: "SPDXRef-Bottle-#{name}",
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    runtime + patches + base
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig {
 | 
			
		||||
    params(runtime_dependency_declaration: T::Array[Hash],
 | 
			
		||||
           compiler_declaration:           Hash).returns(T::Array[T::Hash[Symbol,
 | 
			
		||||
                                                                          T.any(String,
 | 
			
		||||
                                                                                T::Array[T::Hash[Symbol, String]])]])
 | 
			
		||||
  }
 | 
			
		||||
  def generate_packages_json(runtime_dependency_declaration, compiler_declaration)
 | 
			
		||||
    bottle = []
 | 
			
		||||
    if get_bottle_info(source[:bottle])
 | 
			
		||||
      bottle << {
 | 
			
		||||
        SPDXID:           "SPDXRef-Bottle-#{name}",
 | 
			
		||||
        name:             name.to_s,
 | 
			
		||||
        versionInfo:      stable_version.to_s,
 | 
			
		||||
        filesAnalyzed:    false,
 | 
			
		||||
        licenseDeclared:  assert_value(nil),
 | 
			
		||||
        builtDate:        source_modified_time.to_s,
 | 
			
		||||
        licenseConcluded: license,
 | 
			
		||||
        downloadLocation: T.must(get_bottle_info(source[:bottle]))["url"],
 | 
			
		||||
        copyrightText:    assert_value(nil),
 | 
			
		||||
        externalRefs:     [
 | 
			
		||||
          {
 | 
			
		||||
            referenceCategory: "PACKAGE-MANAGER",
 | 
			
		||||
            referenceLocator:  "pkg:brew/#{tap}/#{name}@#{stable_version}",
 | 
			
		||||
            referenceType:     "purl",
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        checksums:        [
 | 
			
		||||
          {
 | 
			
		||||
            algorithm:     "SHA256",
 | 
			
		||||
            checksumValue: T.must(get_bottle_info(source[:bottle]))["sha256"],
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
      {
 | 
			
		||||
        SPDXID:           "SPDXRef-Archive-#{name}-src",
 | 
			
		||||
        name:             name.to_s,
 | 
			
		||||
        versionInfo:      stable_version.to_s,
 | 
			
		||||
        filesAnalyzed:    false,
 | 
			
		||||
        licenseDeclared:  assert_value(nil),
 | 
			
		||||
        builtDate:        source_modified_time.to_s,
 | 
			
		||||
        licenseConcluded: assert_value(license),
 | 
			
		||||
        downloadLocation: source[:stable][:url],
 | 
			
		||||
        copyrightText:    assert_value(nil),
 | 
			
		||||
        externalRefs:     [],
 | 
			
		||||
        checksums:        [
 | 
			
		||||
          {
 | 
			
		||||
            algorithm:     "SHA256",
 | 
			
		||||
            checksumValue: source[:stable][:checksum].to_s,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ] + runtime_dependency_declaration + compiler_declaration.values + bottle
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Array[T::Hash[Symbol, T.any(T::Boolean, String, T::Array[T::Hash[Symbol, String]])]]) }
 | 
			
		||||
  def full_spdx_runtime_dependencies
 | 
			
		||||
    return [] unless @runtime_dependencies.present?
 | 
			
		||||
 | 
			
		||||
    @runtime_dependencies.compact.filter_map do |dependency|
 | 
			
		||||
      next unless dependency.present?
 | 
			
		||||
 | 
			
		||||
      bottle_info = get_bottle_info(dependency["bottle"])
 | 
			
		||||
      next unless bottle_info.present?
 | 
			
		||||
 | 
			
		||||
      {
 | 
			
		||||
        SPDXID:           "SPDXRef-Package-SPDXRef-#{dependency["name"].tr("/", "-")}-#{dependency["version"]}",
 | 
			
		||||
        name:             dependency["name"],
 | 
			
		||||
        versionInfo:      dependency["pkg_version"],
 | 
			
		||||
        filesAnalyzed:    false,
 | 
			
		||||
        licenseDeclared:  assert_value(nil),
 | 
			
		||||
        licenseConcluded: assert_value(dependency["license"]),
 | 
			
		||||
        downloadLocation: assert_value(bottle_info.present? ? bottle_info["url"] : nil),
 | 
			
		||||
        copyrightText:    assert_value(nil),
 | 
			
		||||
        checksums:        [
 | 
			
		||||
          {
 | 
			
		||||
            algorithm:     "SHA256",
 | 
			
		||||
            checksumValue: assert_value(bottle_info.present? ? bottle_info["sha256"] : nil),
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        externalRefs:     [
 | 
			
		||||
          {
 | 
			
		||||
            referenceCategory: "PACKAGE-MANAGER",
 | 
			
		||||
            referenceLocator:  "pkg:brew/#{dependency["full_name"]}@#{dependency["version"]}",
 | 
			
		||||
            referenceType:     :purl,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Hash[Symbol, T.any(String, T::Array[T::Hash[Symbol, String]])]) }
 | 
			
		||||
  def to_spdx_sbom
 | 
			
		||||
    runtime_full = full_spdx_runtime_dependencies
 | 
			
		||||
 | 
			
		||||
    compiler_info = {
 | 
			
		||||
      "SPDXRef-Compiler" => {
 | 
			
		||||
        SPDXID:           "SPDXRef-Compiler",
 | 
			
		||||
        name:             compiler.to_s,
 | 
			
		||||
        versionInfo:      assert_value(built_on["xcode"]),
 | 
			
		||||
        filesAnalyzed:    false,
 | 
			
		||||
        licenseDeclared:  assert_value(nil),
 | 
			
		||||
        licenseConcluded: assert_value(nil),
 | 
			
		||||
        copyrightText:    assert_value(nil),
 | 
			
		||||
        downloadLocation: assert_value(nil),
 | 
			
		||||
        checksums:        [],
 | 
			
		||||
        externalRefs:     [],
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if stdlib.present?
 | 
			
		||||
      compiler_info["SPDXRef-Stdlib"] = {
 | 
			
		||||
        SPDXID:           "SPDXRef-Stdlib",
 | 
			
		||||
        name:             stdlib,
 | 
			
		||||
        versionInfo:      stdlib,
 | 
			
		||||
        filesAnalyzed:    false,
 | 
			
		||||
        licenseDeclared:  assert_value(nil),
 | 
			
		||||
        licenseConcluded: assert_value(nil),
 | 
			
		||||
        copyrightText:    assert_value(nil),
 | 
			
		||||
        downloadLocation: assert_value(nil),
 | 
			
		||||
        checksums:        [],
 | 
			
		||||
        externalRefs:     [],
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    packages = generate_packages_json(runtime_full, compiler_info)
 | 
			
		||||
    {
 | 
			
		||||
      SPDXID:            "SPDXRef-DOCUMENT",
 | 
			
		||||
      spdxVersion:       "SPDX-2.3",
 | 
			
		||||
      name:              "SBOM-SPDX-#{name}-#{stable_version}",
 | 
			
		||||
      creationInfo:      {
 | 
			
		||||
        created:  DateTime.now.to_s,
 | 
			
		||||
        creators: ["Tool: https://github.com/homebrew/brew@#{homebrew_version}"],
 | 
			
		||||
      },
 | 
			
		||||
      dataLicense:       "CC0-1.0",
 | 
			
		||||
      documentNamespace: "https://formulae.brew.sh/spdx/#{name}-#{stable_version}.json",
 | 
			
		||||
      documentDescribes: packages.map { |dependency| dependency[:SPDXID] },
 | 
			
		||||
      files:             [],
 | 
			
		||||
      packages:,
 | 
			
		||||
      relationships:     generate_relations_json(runtime_full, compiler_info),
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(deps: T::Array[Formula]).returns(T::Array[T::Hash[Symbol, String]]) }
 | 
			
		||||
  def self.runtime_deps_hash(deps)
 | 
			
		||||
    deps.map do |dep|
 | 
			
		||||
      {
 | 
			
		||||
        full_name:         dep.full_name,
 | 
			
		||||
        name:              dep.name,
 | 
			
		||||
        version:           dep.version.to_s,
 | 
			
		||||
        revision:          dep.revision,
 | 
			
		||||
        pkg_version:       dep.pkg_version.to_s,
 | 
			
		||||
        declared_directly: true,
 | 
			
		||||
        license:           SPDX.license_expression_to_string(dep.license),
 | 
			
		||||
        bottle:            dep.bottle_hash,
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  sig { params(base: T.nilable(T::Hash[String, Hash])).returns(T.nilable(T::Hash[String, String])) }
 | 
			
		||||
  def get_bottle_info(base)
 | 
			
		||||
    return unless base.present?
 | 
			
		||||
    return unless base.key?("files")
 | 
			
		||||
 | 
			
		||||
    T.must(base["files"])[Utils::Bottles.tag.to_sym]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def stable?
 | 
			
		||||
    spec == :stable
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(Symbol) }
 | 
			
		||||
  def compiler
 | 
			
		||||
    @compiler || DevelopmentTools.default_compiler
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(CxxStdlib) }
 | 
			
		||||
  def cxxstdlib
 | 
			
		||||
    # Older sboms won't have these values, so provide sensible defaults
 | 
			
		||||
    lib = stdlib.to_sym if stdlib
 | 
			
		||||
    CxxStdlib.create(lib, compiler.to_sym)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def built_bottle?
 | 
			
		||||
    built_as_bottle && !poured_from_bottle
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T::Boolean) }
 | 
			
		||||
  def bottle?
 | 
			
		||||
    built_as_bottle
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(Tap)) }
 | 
			
		||||
  def tap
 | 
			
		||||
    tap_name = source[:tap]
 | 
			
		||||
    Tap.fetch(tap_name) if tap_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(Symbol) }
 | 
			
		||||
  def spec
 | 
			
		||||
    source[:spec].to_sym
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(T.nilable(Version)) }
 | 
			
		||||
  def stable_version
 | 
			
		||||
    source[:stable][:version]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(Time) }
 | 
			
		||||
  def source_modified_time
 | 
			
		||||
    Time.at(@source_modified_time || 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(val: T.untyped).returns(T.any(String, Symbol)) }
 | 
			
		||||
  def assert_value(val)
 | 
			
		||||
    return :NOASSERTION.to_s unless val.present?
 | 
			
		||||
 | 
			
		||||
    val
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										60
									
								
								Library/Homebrew/test/sbom_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								Library/Homebrew/test/sbom_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "sbom"
 | 
			
		||||
 | 
			
		||||
RSpec.describe SBOM, :needs_network do
 | 
			
		||||
  describe "#valid?" do
 | 
			
		||||
    it "returns true if the SBOM is valid" do
 | 
			
		||||
      f = formula do
 | 
			
		||||
        url "foo-1.0"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sbom = described_class.create(f)
 | 
			
		||||
      expect(sbom).to be_valid
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns true if the SBOM is valid with dependencies" do
 | 
			
		||||
      f = formula do
 | 
			
		||||
        url "foo-1.0"
 | 
			
		||||
 | 
			
		||||
        # some random dependencies to test with
 | 
			
		||||
        depends_on "cmake" => :build
 | 
			
		||||
        depends_on "beanstalkd"
 | 
			
		||||
 | 
			
		||||
        uses_from_macos "python" => :build
 | 
			
		||||
        uses_from_macos "zlib"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      beanstalkd = formula "beanstalkd" do
 | 
			
		||||
        url "one-1.1"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      zlib = formula "zlib" do
 | 
			
		||||
        url "two-1.1"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      allow(f).to receive_messages(
 | 
			
		||||
        runtime_formula_dependencies: [beanstalkd, zlib],
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      sbom = described_class.create(f)
 | 
			
		||||
      expect(sbom).to be_valid
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns true if SBOM is valid with patches" do
 | 
			
		||||
      f = formula do
 | 
			
		||||
        homepage "https://brew.sh"
 | 
			
		||||
 | 
			
		||||
        url "https://brew.sh/test-0.1.tbz"
 | 
			
		||||
        sha256 TEST_SHA256
 | 
			
		||||
 | 
			
		||||
        patch do
 | 
			
		||||
          url "patch_macos"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      sbom = described_class.create(f)
 | 
			
		||||
      expect(sbom).to be_valid
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user