Skip to content

Commit

Permalink
Merge pull request #66 from macblazer/65-fix-length-of-author-and-pub…
Browse files Browse the repository at this point in the history
…lisher-in-order-to-upload-to-dt

Fix length of author, publisher, and purl in order to upload to Dependency Track
  • Loading branch information
macblazer authored Feb 8, 2024
2 parents bd31b91 + 6579cab commit 8a7a2cd
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 86 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Check license headers
uses: apache/[email protected]
with:
Expand All @@ -45,6 +45,6 @@ jobs:
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Unit tests
run: bundle install; bundle exec rake
8 changes: 7 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ AllCops:
Exclude:
- 'spec/fixtures/**/*'

# Allow RSpec files to have long blocks for the tests.
# Allow RSpec files to have long blocks for the unit tests.
Metrics/BlockLength:
AllowedMethods: ['describe', 'context', 'shared_examples']

# Allow some long methods because breaking them up doesn't help anything.
Metrics/MethodLength:
AllowedMethods: ['parse_options', 'add_to_bom']
Metrics/AbcSize:
AllowedMethods: ['parse_options', 'add_to_bom']
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Added optional `--shortened-strings` CLI parameter to limit the author, publisher, and purl lengths. ([Issue #65](https://github.com/CycloneDX/cyclonedx-cocoapods/issues/65)) [@macblazer](https://github.com/macblazer).

## [1.2.0]

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ OPTIONS
-o, --output bom_file_path Path to output the bom.xml file to (default: "bom.xml")
-b, --bom-version bom_version Version of the generated BOM (default: "1")
-x, --exclude-test-targets Eliminate Podfile targets whose name contains the word "test"
-s, --shortened-strings length Trim author, publisher, and purl to <length> characters; this may cause data loss but can improve compatibility with other systems
Component Metadata
-n, --name name (If specified version and type are also required) Name of the component for which the BOM is generated
Expand Down
131 changes: 94 additions & 37 deletions lib/cyclonedx/cocoapods/bom_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,54 @@ class Pod
CHECKSUM_ALGORITHM = 'SHA-1'
HOMEPAGE_REFERENCE_TYPE = 'website'

def source_qualifier
return '' if source.nil? || source.source_qualifier.empty?

"?#{source.source_qualifier.map do |key, value|
"#{key}=#{CGI.escape(value)}"
end.join('&')}"
end

def purl_subpath
return '' unless name.split('/').length > 1

"##{name.split('/').drop(1).map do |component|
CGI.escape(component)
end.join('/')}"
end

def purl
purl_name = CGI.escape(name.split('/').first)
source_qualifier = source.nil? || source.source_qualifier.empty? ? '' : "?#{source.source_qualifier.map { |key, value| "#{key}=#{CGI.escape(value)}" }.join('&')}"
purl_subpath = name.split('/').length > 1 ? "##{name.split('/').drop(1).map { |component| CGI.escape(component) }.join('/')}" : ''
return "pkg:cocoapods/#{purl_name}@#{CGI.escape(version.to_s)}#{source_qualifier}#{purl_subpath}"
src_qualifier = source_qualifier
subpath = purl_subpath
"pkg:cocoapods/#{purl_name}@#{CGI.escape(version.to_s)}#{src_qualifier}#{subpath}"
end

def add_to_bom(xml)
def xml_add_author(xml, trim_strings_length)
return if author.nil?

if trim_strings_length.zero?
xml.author author
xml.publisher author
else
xml.author author.slice(0, trim_strings_length)
xml.publisher author.slice(0, trim_strings_length)
end
end

def xml_add_homepage(xml)
return if homepage.nil?

xml.externalReferences do
xml.reference(type: HOMEPAGE_REFERENCE_TYPE) do
xml.url homepage
end
end
end

def add_to_bom(xml, trim_strings_length = 0)
xml.component(type: 'library') do
xml.author author unless author.nil?
xml.publisher author unless author.nil?
xml_add_author(xml, trim_strings_length)
xml.name name
xml.version version.to_s
xml.description { xml.cdata description } unless description.nil?
Expand All @@ -83,15 +120,13 @@ def add_to_bom(xml)
license.add_to_bom(xml)
end
end
xml.purl purl
xml.bomRef purl
unless homepage.nil?
xml.externalReferences do
xml.reference(type: HOMEPAGE_REFERENCE_TYPE) do
xml.url homepage
end
end
if trim_strings_length.zero?
xml.purl purl
else
xml.purl purl.slice(0, trim_strings_length)
end
xml.bomRef purl
xml_add_homepage(xml)
end
end

Expand Down Expand Up @@ -128,32 +163,50 @@ def initialize(pods:, component: nil, dependencies: nil)
@dependencies = dependencies&.sort
end

def bom(version: 1)
raise ArgumentError, "Incorrect version: #{version} should be an integer greater than 0" unless version.to_i > 0
def bom(version: 1, trim_strings_length: 0)
unless version.to_i.positive?
raise ArgumentError,
"Incorrect version: #{version} should be an integer greater than 0"
end

unless trim_strings_length.is_a?(Integer) && (trim_strings_length.positive? || trim_strings_length.zero?)
raise ArgumentError,
"Incorrect string length: #{trim_strings_length} should be an integer greater than 0"
end

unchecked_bom(version: version, trim_strings_length: trim_strings_length)
end

private

# does not verify parameters because the public method does that.
def unchecked_bom(version: 1, trim_strings_length: 0)
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
xml.bom('xmlns': NAMESPACE, 'version': version.to_i.to_s, 'serialNumber': "urn:uuid:#{SecureRandom.uuid}") do
xml.bom(xmlns: NAMESPACE, version: version.to_i.to_s, serialNumber: "urn:uuid:#{SecureRandom.uuid}") do
bom_metadata(xml)
xml.components do
pods.each do |pod|
pod.add_to_bom(xml)
end
end

xml.dependencies do
bom_dependencies(xml, dependencies)
end
bom_components(xml, pods, trim_strings_length)

bom_dependencies(xml, dependencies)
end
end.to_xml
end

private
def bom_components(xml, pods, trim_strings_length)
xml.components do
pods.each do |pod|
pod.add_to_bom(xml, trim_strings_length)
end
end
end

def bom_dependencies(xml, dependencies)
dependencies&.each do |key, array|
xml.dependency(ref: key) do
array.sort.each do |value|
xml.dependency(ref: value)
xml.dependencies do
dependencies&.each do |key, array|
xml.dependency(ref: key) do
array.sort.each do |value|
xml.dependency(ref: value)
end
end
end
end
Expand All @@ -162,14 +215,18 @@ def bom_dependencies(xml, dependencies)
def bom_metadata(xml)
xml.metadata do
xml.timestamp Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ')
xml.tools do
xml.tool do
xml.vendor 'CycloneDX'
xml.name 'cyclonedx-cocoapods'
xml.version VERSION
end
bom_tools(xml)
component&.add_to_bom(xml)
end
end

def bom_tools(xml)
xml.tools do
xml.tool do
xml.vendor 'CycloneDX'
xml.name 'cyclonedx-cocoapods'
xml.version VERSION
end
component.add_to_bom(xml) unless component.nil?
end
end
end
Expand Down
Loading

0 comments on commit 8a7a2cd

Please sign in to comment.