Module: StillActive::SbomReader
Overview
Reads a CycloneDX SBOM (e.g. produced by Syft) into a clean, normalized dependency set -- the breadth input for non-Ruby ecosystems, where the maintenance lens runs over deps.dev/ecosyste.ms.
parse returns both the assessable dependencies AND the components it could
NOT assess (a real dependency in an unsupported ecosystem, or a library with
no PURL -- typically a git/path/local source). Surfacing those is the point:
silently dropping them would let an audit report "all clear" while ignoring
the deps that are often the riskiest. Genuine non-package noise (GitHub
Actions pkg:github, opaque binaries pkg:generic, and file/application
components) is excluded, not surfaced.
read keeps the simple "just the dependencies" shape. Never raises -- a
missing/malformed SBOM or a bad PURL degrades to empty/skip.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- ECOSYSTEMS =
PURL type -> still_active ecosystem (also the deps.dev
system, lowercased). { "gem" => :rubygems, "npm" => :npm, "pypi" => :pypi, "cargo" => :cargo, "maven" => :maven, "golang" => :go, "nuget" => :nuget, }.freeze
- NOISE_TYPES =
PURL types that are not package dependencies: CI actions and opaque binaries. Excluded from both lists (not "unassessed deps").
["github", "generic"].freeze
Instance Method Summary collapse
Instance Method Details
#parse(path) ⇒ Object
45 46 47 48 49 |
# File 'lib/still_active/sbom_reader.rb', line 45 def parse(path) parse_string(File.read(path)) rescue SystemCallError, IOError Result.new(dependencies: [], unassessable: []) end |
#parse_string(body) ⇒ Object
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 |
# File 'lib/still_active/sbom_reader.rb', line 51 def parse_string(body) doc = JSON.parse(body) components = doc.is_a?(Hash) ? doc["components"] : nil return Result.new(dependencies: [], unassessable: []) unless components.is_a?(Array) # Only trust the prod/dev split when the generator actually marks it # somewhere (scope, or a dev property). syft-style SBOMs mark nothing, so an # unmarked component is genuinely unknown -- never assume it's production. # Scoped to library components: scope/properties are legal on any component # type (application/file/...), and a dev signal on one of those is unrelated # to whether the dependency library components are marked. marks_dev = components.any? { |c| c.is_a?(Hash) && c["type"] == "library" && dev_signal?(c) } deps = [] unassessable = [] components.each do |component| kind, entry = classify(component) entry[:production] = !dev_signal?(component) if kind == :dependency && marks_dev deps << entry if kind == :dependency unassessable << entry if kind == :unassessable end # uniq collapses a package that a generator lists more than once (e.g. Syft's # per-location entries); `production` is derived from each component's own # signal, so duplicates of the same name+version dedup cleanly unless a # generator marks the copies inconsistently (malformed; not observed). Result.new(dependencies: deps.uniq, unassessable: unassessable.uniq) rescue JSON::ParserError Result.new(dependencies: [], unassessable: []) end |
#read(path) ⇒ Object
42 |
# File 'lib/still_active/sbom_reader.rb', line 42 def read(path) = parse(path).dependencies |
#read_string(body) ⇒ Object
43 |
# File 'lib/still_active/sbom_reader.rb', line 43 def read_string(body) = parse_string(body).dependencies |