Compare commits

..
Author SHA1 Message Date
Alex Goodman 07c2c816c6 fix: keep embedded SBOM relationships from adding files to the scan
The bitnami and `sbom` catalogers decode SBOMs found in the scanned image and kept the relationships from them. The decoder nests a file under another the SBOM says CONTAINS it, and makes a file the root package DESCRIBES the scan root, so an embedded SBOM could inject archive chains or root files that encoders then rebuilt in the output.

- both catalogers now drop relationships with a file endpoint that is nested or at the scan root
- bitnami also keeps only relationships from its own packages

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-29 15:50:03 -04:00
Alex Goodman c38a68e9de fix: hold archive chains to the same bounds when scanning and decoding
Decoding SPDX, CycloneDX and syft-json rebuilt nested archive chains from whatever the document said, with no checks on entry names, depth or length. A small crafted document could forge archive boundaries or cost memory quadratic in its size (a 3 MB SPDX allocated ~14 GB).

- decoders now hold names to the scan's `EntryName` rules, and chains to `MaxDepth` (16) and PATH_MAX in length
- a file within a refused chain is dropped, rather than moved to a top-level path or merged into its outer archive
- the scan holds itself to the same bounds, so its SBOMs always decode back: a configured `nested-archive-max-depth` past 16 stops at 16, and an entry whose chain would be too long is skipped and the archive reported as truncated

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-29 15:50:03 -04:00
Keith Zantow 071d7a4461 fix: spdx and cyclonedx
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-28 21:30:36 -04:00
Keith Zantow 1344a5f598 fix: properly read mimetype
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-28 17:28:01 -04:00
Keith Zantow cc7693683f fix: archive scan leaking temp directories
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-28 13:26:51 -04:00
Keith Zantow 851dc327c5 fix: file selection within archives, spdx / cyclonedx file representation
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-27 14:52:18 -04:00
Keith Zantow 6d34eba977 chore: correct README
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-22 11:15:03 -04:00
Keith Zantow 3149fffada fix: do not use AccessPath / Location.Path() except in special cases
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-22 10:17:02 -04:00
Alex Goodman deedaf6125 chore: fold schema 16.1.13 back into 16.1.12
16.1.12 was never released, and 16.1.13 differed from it only in field descriptions, which per
`schema/json/README.md` don't get a version of their own.

- drop `schema-16.1.13.json` and set `JSONSchemaVersion` back to `16.1.12`
- regenerate `schema-16.1.12.json` and `schema-latest.json` so the `fullPath` descriptions are current
- the 16.1.12 changelog line now describes `fullPath` instead of the old `archivePath`

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:46:45 -04:00
Alex Goodman 72835436c4 fix: keep unread nested archive limits out of java capabilities
- java embeds the shared `cataloging.ArchiveSearchConfig` but only reads the two `Include*` booleans,
  and setting depth or byte limits there fails `CreateSBOM`
- a `capabilities-fields:` annotation on an embedded struct now limits which of its fields are
  listed, so the docs stop advertising settings that error
- building the java cataloger directly with those fields set now warns instead of debug logging

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:45:41 -04:00
Alex Goodman 36d417d52a fix: charge the decompression budget at the decoder, not per entry
Draining skipped entries still left a hole: a tar entry whose header doesn't read as a regular file
(a socket or directory per its mode bits, say) can carry a body, and the tar reader decompresses it
to reach the next header without anything being charged or ctx being checked.

- compressed tars are now read through a reader that charges the budget for every byte the decoder
  produces and checks ctx per read, so no skip path inside the tar reader gets around it
- entry content from that stream isn't charged a second time, and the per-entry drain is gone
- zip and plain tar are unchanged

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:45:34 -04:00
Alex Goodman c4c0907d4e fix: keep panic stacks out of archive unknowns
A recovered panic while cataloging an archive became an unknown whose reason carried the full
goroutine stack, so a crafted archive that panicked a decoder put kilobytes of stack and build paths
into the published SBOM.

- the reason is now just `panic while cataloging archive: <value>`
- the stack is logged at debug, matching `runSubTaskSafely`

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:42:23 -04:00
Alex Goodman 432f9b0803 fix: charge the budget for compressed tar bytes that are skipped, not just kept
The tree decompression budget was only spent as entry content was stored. A compressed tar reader
still decompresses whatever an entry leaves unread to reach the next header, so an excluded entry, a
file/directory clash, or the rest of one over the 2 GB per-entry cap decompressed with nothing charged
and no ctx check. A small `.tar.zst` could force hundreds of GB of work past the budget.

- in compressed tars the rest of every regular entry is now drained through the budget, with ctx
  checked per chunk
- zip and plain tar are unchanged: they seek past skipped entries, so those cost nothing

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:41:32 -04:00
Alex Goodman 2fa7282abc fix: let the last archive entry at a path win
When an archive held two entries at one path, the resolver kept the first, and this was documented as
intended. Everything that actually unpacks the archive keeps the last: `tar` (and `tar -r` appends
updates), OCI layer application, and the legacy java path's `ContentsFromZip`. So a jar could carry a
`pom.properties` reporting 2.17.1 ahead of one installing 2.14.0, and the SBOM would show the former.

- a later regular file, link or other non-directory entry now replaces the earlier one, refunding its
  in-memory bytes
- a file/directory clash at one path still keeps the first, since swapping kinds would mean
  re-parenting what is already under it

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:39:55 -04:00
Alex Goodman 78f1d44e3c fix: restore case-insensitive java globbing for licenses, version and nested jars
The `archiveEntries` refactor dropped `GlobMatch`'s case-insensitive flag, so with nested cataloging
off (the default) three lookups that main matched regardless of case became case-sensitive:

- license files under `/META-INF/*` and `/*` (`meta-inf/LICENSE` was missed)
- `/version.properties`
- the `**/*.jar`, `**/*.war` check behind the "nested archives not cataloged" unknown, so `LIB.JAR`
  slipped past it

`archiveEntries` now has a `globFold` for those call sites. The zip side restores
`GlobMatch(true, ...)`, and the resolver side matches case-folded over every entry, since resolver
globs are case-sensitive.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:37:45 -04:00
Alex Goodman abc9325081 fix: restore conaninfo's host path lookup
The conaninfo parser reads the package name and version out of the
`<name>/<ver>/<user>/<channel>/package/<id>/conaninfo.txt` layout. It used to read that from the file
reference's real path (the absolute host path) and fall back to the location, but the path sweep
switched it to `LocalPath()` only. A single file scan, or a directory scan rooted below
`.conan/data`, then failed to parse the path and dropped the package.

The reference's real path is used first again, falling back to `LocalPath()` (which is what nested
archive entries have, since they carry no reference).

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:36:17 -04:00
Alex Goodman 01546d4e06 fix: don't index nested packages under the outer archive path
`indexedPaths` added each location's `RealPath`, which for a nested file is the outermost archive. So
`PackagesByPath("/opt/x.jar")` returned every package found inside it at any depth, a root OS package
owning that file got ownership-overlap edges to all of them, and
`ExcludeBinaryPackagesWithFileOwnershipOverlap` then deleted nested binary, ELF and JVM packages.

Packages are now indexed by full path and access path only. An archive's own package still sits at
root coordinates and is found by its path; what's inside it stays linked to it by CONTAINS edges
rather than by ownership of the archive file.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:35:21 -04:00
Alex Goodman f0129112be fix: normalize decoded FullPath
Nothing checked that a decoded `FullPath` actually extends `RealPath` with an archive chain. The SPDX
decoder sets `FullPath` to the file name even when the name doesn't end with the comment's chain, and
syft-json takes whatever is in the document. `{RealPath: "/x", FullPath: "/x"}` and `{RealPath: "/x"}`
share an ID but are different map keys, so decoded files, unknowns and locations could fail to line
up with the same coordinates built by a resolver.

The syft-json and SPDX decoders now clear a `FullPath` that equals `RealPath` or doesn't start with
`RealPath` + `!/`.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:32:50 -04:00
Alex Goodman b9fdabc2f6 fix: make Location.Path() always the full path
`Path()` fell back to `LocalPath()` when `AccessPath` was blank, so for a nested file it returned the
full chain from a resolver but the archive-local path from a hand-built or decoded location, and
callers couldn't tell which they had.

- `Path()` now falls back to `FullNestedPath()`. Root files are unchanged
- the maven parent pom lookup joins `relativePath` onto `LocalPath()`, since enough `../` on the full
  path cleaned past the `!/` boundary into an outer path that the archive resolver then read as
  archive-local

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:30:31 -04:00
Alex Goodman 901d7bbab2 fix: don't infer archive nesting from paths in error text
`ProcessPathErrors` cut the `path="..."` it pulled out of an error at the first `!/` and built nested
coordinates from it, so a host file with `!/` in its name was reported as a file inside an archive.

- paths from error text are taken as plain real paths again, as on main
- the php extension reader already has the location in hand, so it records the unknown against that
  instead of re-parsing the error (this is the one caller that could see a nested path). It also
  no longer defers `Close` on a nil reader when the read fails

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:28:53 -04:00
Alex Goodman 99723d91dd fix: don't split real paths on !/ in LocalPath and ContainerPath
`LocalPath` searched `FullNestedPath()` for the last `!/`, which is just `RealPath` for a file not in
an archive. A host file like `/data/Hello!/world.txt` came back as `/world.txt`, so lookups and
exclusions ran against the wrong path even with nested cataloging off. `ContainerPath` had the same
problem when `FullPath` was set to `RealPath`.

Both now cut `RealPath` off first and only look for a separator in the entry chain after it, which
can't contain `!/` since entry names with it are refused.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:23:34 -04:00
Alex Goodman 2897361337 fix: cap archive entry path depth
Adding an entry checked `HasPath` on every prefix of its path, so a name with thousands of
components did O(depth^2) hashing per entry before anything was charged.

- entry names over 256 path components are refused and reported like other unsafe names
- implied parent directories are found walking from the deepest parent up, stopping at the first
  one that already exists

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:21:36 -04:00
Alex Goodman 7717baa43d fix: refuse archive entry names that forge paths or lines
`entryName` only rejected NUL and over-long names, so whatever an archive called an entry flowed
straight into `FullPath`, the file tree and every encoder.

- a name containing `!/` could pose as a nested archive boundary and collide with a real nested entry
- control characters and unicode line separators could add lines to SPDX tag-value output

Both are now refused, and refused names (over-long ones included) mark the resolver truncated with
the quoted name instead of being dropped silently.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-10-21 11:20:30 -04:00
Keith Zantow a1f7c52d89 fix: unknowns, better full vs local path handling
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-20 11:15:17 -04:00
Keith Zantow 3cdf97ebad chore: bump schema
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-19 16:31:31 -04:00
Keith Zantow 9d0cd75456 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-10-19 16:13:17 -04:00
Keith Zantow 3e4b036e22 fix: keep RealPath as top-level, use FullPath and utility functions FullNestedPath, LocalPath, Path
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-19 15:44:37 -04:00
Keith Zantow 17d109e8b5 chore: remove custom index in favor of existing filetree.Index
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-10-13 16:22:32 -04:00
Keith Zantow a707a7bfd9 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-10-12 09:25:49 -04:00
Keith Zantow 702d7d6100 chore: update failing tests
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-10-08 13:16:42 -04:00
Keith Zantow 48c2d9b300 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-10-08 09:29:45 -04:00
Keith Zantow 5abeacdb88 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-10-05 15:42:07 -04:00
Keith Zantow 959e8cbb7b chore: update capabilities
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-30 16:14:54 -04:00
Keith Zantow fda2fe4fcc chore: archive memory and disk limits of 0 mean 0
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-30 15:57:48 -04:00
Keith Zantow d95abbf6b5 chore: truncation reason to error type
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-30 15:52:45 -04:00
Keith Zantow b2942743a5 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-30 15:31:47 -04:00
Keith Zantow 12d3097741 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-30 14:55:38 -04:00
Alex Goodman 8e2cdf2429 chore: drop the locking and JSON support from internal/index
the index was ported from a prototype with per-node `RWMutex` lock upgrading and JSON marshalling. Its only consumer (`archive.Resolver`) builds it sequentially during extraction and then only reads it, so neither was used. `lock.go` also compared method-value pointers to tell the read lock from the write lock, which can't distinguish lock instances.

removed the locks, the upgrade and retry paths that existed only for concurrent writers, and the JSON methods and their test. The radix logic is unchanged, and the README now points at `archive.Resolver` rather than a nonexistent `archive.Index`.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:42:59 -04:00
Alex Goodman 9dbef8e831 fix: keep archive CONTAINS package edges in SPDX
the SPDX writer only kept relationships whose `From` is a package, so the `file CONTAINS package` edges the archive task records were dropped. For anything that isn't java (npm in a tgz, python in a zip) that edge is the only thing saying which archive a package came out of.

SPDX now also writes `CONTAINS` from a file to a package. The file end is always in the document, since `toFiles` covers every coordinate a relationship names. Other file-side relationships (e.g. binary's file `DEPENDENCY_OF` package) are still dropped as before.

CycloneDX is unchanged: it has no file components to hang the edge on, and war-to-jar structure reaches it through the java `DEPENDENCY_OF` parity fix.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:41:39 -04:00
Alex Goodman b735b615f0 fix: name nested files in SPDX by their archive chain
SPDX wrote `FileName` from `RealPath` only, so two archives' `META-INF/MANIFEST.MF` became two SPDX files with the same name and nothing saying which archive each came from. Worse, a consumer reading just the name would take a nested file for one at that path in the scan root.

- a file inside an archive is now named by its full chain, using the same `:` convention and escaping as `ArchivePath` / java's `VirtualPath`: `app.war:WEB-INF/lib/a.jar:META-INF/MANIFEST.MF`
- `FileComment` gains an `archivePath: ...` line (next to the existing `layerID: ...`, one per line), so decoding recovers `ArchivePath` and the entry's own `RealPath` without guessing where the chain ends
- files outside archives are unchanged

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:40:18 -04:00
Alex Goodman 47d4b665cf fix: only skip META-INF/maven poms inside an extracted java archive
`isArchiveMetaPom` was an unconditional `strings.Contains(path, "META-INF/maven/")`, so the pom cataloger dropped real poms everywhere, even with the feature off: exploded wars (`WEB-INF/classes/META-INF/maven/...`), test fixtures under `src/test/resources`, source tarballs. That's a regression on the default path.

the skip exists because inside a jar those poms are raw build copies that java already covers from `pom.properties`. So it now applies only when the traversal on the context is a java archive. Outside one, or inside a tarball java doesn't describe, the poms are cataloged as before this PR.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:38:19 -04:00
Alex Goodman 22edfe66ca fix: link nested java packages to their enclosing jar when nested cataloging is on
the legacy recursion links a nested jar to the jar that holds it: a `DEPENDENCY_OF` edge from inner to outer, and `JavaArchive.Parent` on the inner package. Inside an extracted archive java called `parse(ctx, nil)`, so both went away as soon as the feature was on. What replaced them was `file CONTAINS package`, which SPDX and CycloneDX drop, so those formats lost all war-to-jar structure.

- `archive.Traversal` gains `Parent` and `Packages` (the packages found describing that archive, filled in by the task after its sub-pipeline runs)
- java picks the enclosing jar's main package from the parent traversal (the one whose virtual path is the archive's own; pom-derived aux packages carry a suffixed one) and passes it to `parse`, then sets `Parent` the same way the legacy path does

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:36:52 -04:00
Alex Goodman 23de18918e fix: run root file catalogers after the archive task with owned-by-package selection
with nested cataloging on, java leaves root-level archives to the archive task, so a root `app.jar` package only exists once that task has run. With the default `FilesOwnedByPackageSelection` the root file catalogers ran in an earlier group, never saw the package, and `app.jar` got no file metadata or digests. It did get them with the feature off.

- in owned-by-package mode the groups are now packages, then the archive task, then the root file tasks. Other modes are unchanged
- the file tasks merge their results into the SBOM instead of replacing the maps, since the archive task has already written nested files into them

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:34:09 -04:00
Alex Goodman 9d820c6d94 fix: only look up owned-by-package files in the resolver's own frame
with `FilesOwnedByPackageSelection` the file catalogers take every package location in the SBOM and look it up by `RealPath` in the resolver they were given. Inside an archive, the java package's location is the archive itself *in its parent's frame* (`WEB-INF/lib/dep.jar`), so the sub-pipeline looked that path up inside `dep.jar`'s own resolver: a miss, or an unrelated same-named entry by coincidence. At the root, coordinates from inside archives were looked up against the scan root the same way.

`coordinatesForSelection` now keeps only coordinates whose `ArchivePath` matches the current frame (the traversal's `ContentsArchivePath()`, or blank at the root).

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:30:33 -04:00
Alex Goodman 0152e5b59c fix: a damaged entry truncates the archive instead of failing it
a read error while copying one entry (a zip CRC mismatch, a corrupt deflate stream) was returned from `add`. That aborted the walk, and `Extract` threw away every entry already stored. So a large war with one damaged, irrelevant entry produced zero packages with the feature on, while java's legacy path (which reads only the entries it needs) still cataloged it.

- a read error on one entry now drops that entry and marks the resolver truncated (`entry "x" could not be read: ...`), and the walk continues
- if the stream itself breaks partway (a truncated `.tar.gz`), whatever was read before the break is kept and the archive is truncated. With nothing read it is still an error, as before
- disk limit and decompression budget errors still stop the walk

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:28:53 -04:00
Alex Goodman 14bae1a0d9 fix: emit archive-to-archive CONTAINS edges and drop self-loops
the archive's `CONTAINS` edges were built from every coordinate in the sub-pipeline's scratch SBOM, which gets one of two things wrong:
- with the default `FilesOwnedByPackageSelection`, nothing records the nested archives as files, so the archive-to-archive chain was missing and grandchild packages hung off coordinates with no incoming edge
- the scratch SBOM also holds coordinates from the parent's frame, most notably unknowns recorded at the archive's own coordinates (e.g. a jar with no manifest). Those turned into `x.jar CONTAINS x.jar`

edges now go to the nested archives found in it (always, whatever the file selection), plus coordinates whose `ArchivePath` is this archive's own chain, i.e. files that are actually inside it. The walk reuses that one discovery pass for the recursion.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:26:59 -04:00
Alex Goodman fe4fdc786c fix: report archives left unexplored at the nested depth limit
with nested cataloging on, java stops recursing into jars itself (the task owns recursion) and its own "nested archives not cataloged" unknown is suppressed. The task then just returned at the depth bound. So `MaxDepth: 1` on a war silently found fewer jars than leaving the feature off, and nothing in the SBOM said so.

at the bound, every archive that sits there (by content sniff) is now recorded as a "nested archive not cataloged: the nested archive depth limit was reached" unknown. The CLI description for `nested-archive-max-depth` notes that it replaces java's unbounded jar-in-jar recursion.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:24:29 -04:00
Alex Goodman 81ee189bf4 fix: bound total decompression under each top-level archive
the limiter bounds what's held *at once*, and each archive gives its bytes back when it's released. So a recursive bomb (42.zip shape) never trips a limit: every level fits on its own, and the scan just runs for effectively forever. Capping each archive at N× its own size wouldn't help either, since every level of such a bomb is high-ratio, so the work still grows about N-fold per level.

- each top-level archive gets one decompression budget of `max(100 × its size, 1 GiB)`, spent by every archive nested under it at any depth
- the budget is attached to the context when the walk enters a top-level archive, so `Extract` picks it up without a signature change
- once it's spent, the archive being extracted is truncated with a "possible decompression bomb" unknown and the walk moves on. The next top-level archive starts fresh

real trees (a war of jars, a layer of tarballs) expand a few times over, well under the ratio.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:23:32 -04:00
Alex Goodman d27b6ebb1b fix: cap each extracted archive entry at 2 GB
`put` had no per-entry cap, unlike the legacy java path which capped every file at 2 GB through `intFile.SafeCopy`. At depth 1, one entry of an overlapping-entry zip bomb could decompress all the way to the 100 GB disk limit before anything stopped it.

- entries are now read through the same 2 GB cap (`intFile.PerFileReadLimit`, now exported). An entry over it is dropped after cap+1 bytes and the rest of the archive is still extracted
- `Resolver` gains `TruncatedReason`, so the truncation unknown says what happened ("an entry larger than 2.0 GiB was skipped", or the existing disk-limit text) instead of always blaming the limits

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 13:21:08 -04:00
Alex Goodman 27655c2be0 fix: bound archive recursion with a cycle guard and depth ceiling
a negative `MaxDepth` really was unbounded, and nothing noticed an archive nested inside a copy of itself. A zip quine at `-1` recursed until memory, disk or file descriptors ran out, running the full sub-pipeline at every level.

- the walk keeps a stack of the digests of the archives it's inside, and an archive identical to one of them is recorded as an unknown instead of being entered
- negative depth now stops at 16 levels (docs and CLI description updated)
- cancellation is checked between candidates, so a cancelled scan stops walking instead of finishing the archive tree

the three unknown-recording blocks in `processArchive` became a one-line helper to stay under `funlen`.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:32:15 -04:00
Alex Goodman 73397f1e8b fix: only extract zip and tar family archives
`identifyFormat` accepted any extractor mholt identified, which put the 7z and rar decoders (`bodgit/sevenzip`, `rardecode`) on untrusted input with no test coverage. Only zip, tar and compressed tar are opened now, until other formats are deliberately supported.

This also fixes java resource adapters: a `.rar` that is really a zip used to be identified by name as RAR and fail to extract. An unsupported identification now falls through to the zip end-of-central-directory check, so it's read as the zip it is.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:29:34 -04:00
Alex Goodman c9b4f9c085 fix: recover from panics while extracting an archive
sub-pipeline catalogers were already run under `runTaskSafely`, but extraction wasn't: the zip, tar and compressor decoders run on untrusted bytes, and a panic in one unwound the whole walk and failed `CreateSBOM` with "failed to run tasks".

- `processArchive` recovers and returns the panic (with its stack) as an error at the archive's coordinates, so it becomes an unknown and the sibling archives are still cataloged
- `Extract` now defers `Cleanup` on failure instead of calling it on each error path, so a panic no longer leaks the limiter charge or the spill file

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:28:26 -04:00
Alex Goodman f5bac4a026 fix: build ArchivePath from real paths, not access paths
each `ArchivePath` segment came from `loc.Path()`, so it was the link path when an archive was reached through a symlink and the real path otherwise. That depended on how discovery happened to find the archive. The package location said `store/3f9a1c` while the files inside the same jar were recorded under `...:app/lib/json-simple-1.1.1.jar`, so a consumer couldn't join a package to its files by prefix.

The chain for extracted files is now built from `RealPath`. `Traversal.Location` still carries the access path, so java's name check and `VirtualPath` metadata behave the same as before. The field comment and schema now say every segment has links resolved.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:26:38 -04:00
Alex Goodman 5fc384dddb fix: keep the access path of archives discovered through a link
`discoverArchives` collects MIME hits first, and those are bare real paths. When the glob pass then found the same file through a symlink, it was dropped as a duplicate along with its access path. Java decides whether to describe an archive by its name, so a content-addressed jar (`store/3f9a1c`, linked from `lib/json-simple-1.1.1.jar`) was extracted but produced no package. This is the pnpm / nix / bazel layout.

When deduping, the candidate now takes the first location that carries an access path.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:25:40 -04:00
Alex Goodman eec86cf88d fix: escape % in ArchivePath segments
`:` was escaped as `%3A`, but `%` itself wasn't, so an entry named `a:b.jar` and one named `a%3Ab.jar` got the same `ArchivePath`. Their contents then collide in `FileMetadata` / `FileDigests` (last writer wins) and share artifact IDs.

`%` is now escaped as `%25` before `:`, which makes the encoding injective. The encoding is documented on `Coordinates.ArchivePath` and in the schema.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:23:54 -04:00
Alex Goodman 78c4485757 fix: make ArchivePath part of file ordering and Location identity
syft-json output wasn't reproducible for nested archives. `AllCoordinates()` comes from map iteration, and the coordinate and location sorters ignored `ArchivePath`, so the same path in two archives (e.g. `META-INF/MANIFEST.MF` in two jars in a war) compared equal and came out in random order. Package locations had the same problem.

- `ArchivePath` is now the final tiebreak in `LocationSorter`, `CoordinatesSorter` and the `CoordinateSet.ToSlice` fallback
- `Location.Equals` and `Location.String` include `ArchivePath`, which matches `Coordinates`, since the same path in different archives is a different file

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:22:52 -04:00
Alex Goodman ccc4832ff6 fix: record archive search in the SBOM descriptor
nothing in the output said nested archive cataloging ran, or how deep it went: `Archive` wasn't in the configuration audit trail, and `archive-cataloger` never showed up in `catalogers.used`.

- the audit trail gains an `archive` section with the top-level archive config
- `catalogers.used` includes `archive-cataloger` when the task is scheduled
- the `packages` section now reports java's config after the archive search booleans are overlaid, i.e. what java actually used

`descriptor.configuration` is untyped in the schema, so no schema change.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:20:04 -04:00
Alex Goodman 3f0a599d31 fix: make CreateSBOMConfig.Archive the one place to configure archive search
`ArchiveSearchConfig` lived in two places and each half was dead in one of them: the top-level booleans were never read (only java's copy was), and java's depth and limits did nothing. A library caller setting `Packages.JavaArchive.MaxDepth = 3` got no nested cataloging, and no error.

- java's embedded `ArchiveSearchConfig` and `WithArchiveTraversal` are deprecated; `WithMaxParentRecursiveDepth` replaces the latter's other half
- `CreateSBOM` now rejects `MaxDepth`, `MaxMemoryBytes` or `MaxDiskBytes` set on java's copy with an `invalid configuration` error
- the top-level `IncludeIndexedArchives` / `IncludeUnindexedArchives` are overlaid onto a copy of java's config, and win when they differ from the default. Callers who only set java's copy see no change
- `java.DefaultArchiveCatalogerConfig()` now carries only the booleans, so its descriptor no longer reports limits that java never used
- the CLI fills only the top-level config, and the option descriptions now say the search booleans are ignored when nested cataloging is on

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:18:51 -04:00
Alex Goodman a4a1d7afa7 fix: treat zero archive limits as the default
`ArchiveSearchConfig{MaxDepth: 2}` is the natural literal, but zero memory and zero disk meant "forbid both", so every archive was truncated to nothing and only unknowns came out.

- `MaxMemoryBytes` / `MaxDiskBytes` of `0` now mean the default, negative is still unbounded
- the limiter keeps its own zero-forbids semantics internally; it's just no longer reachable from config
- tests that relied on zero to force spilling now pass `archive.Limits` directly, and the two "both limits zero" tests are gone since that config no longer exists

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:14:23 -04:00
Alex Goodman fe4a9a287a chore: use only the default title for archive progress
matches how the per-cataloger progress rows are titled (`Default` only), so the archive row reads like its siblings.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-24 11:11:36 -04:00
Keith Zantow cda56d7790 chore: rewrite top-level archive_cataloger_test to avoid repeated CreateSBOM calls
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-22 13:21:27 -04:00
Keith Zantow b36234cfe7 feat: simplify+improve resolver implementation / glob search
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-22 11:32:30 -04:00
Keith Zantow 631972ca1c chore: update capabilities
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-22 09:00:35 -04:00
Keith Zantow dc3976c0a5 chore: update schema
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-22 08:32:29 -04:00
Keith Zantow 8d04b89bee Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-09-22 08:28:04 -04:00
Keith Zantow 47cafae01b chore: simplify and cleanup
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-22 00:23:36 -04:00
Keith Zantow 53cd4254e4 chore: revert custom index in favor of key_Split
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-20 11:22:10 -04:00
Keith Zantow 99615b7dfa chore: update test
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-10 15:04:58 -04:00
Keith Zantow 245fc03887 Merge remote-tracking branch 'upstream/main' into add-nested-archive-cataloging 2026-09-10 13:32:07 -04:00
Keith Zantow 8c0d3747ce feat: initial recursive archive cataloging impl
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-10 08:45:36 -04:00
Keith Zantow 0a93e60b16 chore: revert original recursive-archive implementations from PR #4044 and #4761
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
2026-09-09 17:22:51 -04:00
Keith Zantow 50100f6531 Merge PR #4044: recursive nested-archive scanning at the resolver layer
Adds recursive archive indexing/traversal in the fileresolver and source
provider layers.

Original work by @Rupikz in anchore/syft#4044.

# Conflicts:
#	cmd/syft/internal/options/source.go
#	syft/internal/fileresolver/directory.go
#	syft/internal/fileresolver/filetree_resolver.go
#	syft/internal/fileresolver/filetree_resolver_test.go
#	syft/pkg/cataloger/java/internal/maven/resolver_test.go
#	syft/source/directorysource/directory_source.go
#	test/cli/archive_test.go
2026-06-30 16:50:23 +00:00
Keith Zantow de9f43600f Merge PR #4761: recursive archive cataloging (orchestrator + composite resolver)
Adds internal/archive package (extractor, CompositeResolver, Orchestrator),
wires it into CreateSBOM, plus java pom-cataloger fixes.

Original work by @toabctl in anchore/syft#4761.
2026-06-30 16:42:18 +00:00
Thomas Bechtold 5e5760e132 fix(archive): use writable permissions for extracted directories
Archives may contain directory entries with read-only permissions
(e.g., META-INF/ with mode 0555 in JARs). When extracted to temp
directories, these restricted permissions prevented writing files
into those directories, causing "permission denied" errors for
subsequent file entries like META-INF/MANIFEST.MF.

Always use 0755 for extracted directories since they are temporary
working directories owned by the extraction process.

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:49:46 +02:00
Thomas Bechtold 41a1998d45 fix(java): skip META-INF/maven pom.xml in pom cataloger
The java-pom-cataloger was processing pom.xml files found inside
META-INF/maven/ directories, which are archive metadata embedded by the
Maven build process. These are verbatim copies of the original build POM
containing unresolved ${property} references and dependency versions
inherited from parent POMs that are unavailable inside the archive. This
caused phantom packages with UNKNOWN versions to appear when recursive
archive cataloging opened JARs and exposed their internal pom.xml files
to the pom cataloger.

The java-archive-cataloger already uses pom.properties (which always
contains resolved values) to identify these archives, so the pom
cataloger should skip META-INF/maven/ pom.xml files entirely.

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:49:00 +02:00
Thomas Bechtold bb7dbf2ff8 test(archive): add integration tests for recursive archive cataloging
Add integration tests covering the full archive extraction pipeline:

- tar.gz containing Python package metadata (single-level)
- zip containing a JAR with MANIFEST.MF (two-level, same format)
- Mixed archive types (zip + tar.gz at same level)
- Depth limit enforcement (3-level zip nesting, limited to 1)
- Extension exclusion filtering
- 3-level mixed format nesting: tar.gz -> zip -> tar.gz -> files
  verifying cross-format extraction and full access path chains
- Reverse format crossing: tar.gz -> zip -> tar.gz verifying
  content readability at all levels and correct relationship count

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:49:00 +02:00
Thomas Bechtold 3fc5194f77 feat(sbom): wire archive orchestrator into CreateSBOM pipeline
Integrate the archive extraction orchestrator into the SBOM creation
pipeline, enabling recursive archive cataloging when configured:

- Add Archive field to CreateSBOMConfig with WithArchiveConfig builder
- Add Exclusions field to CreateSBOMConfig with WithExclusions builder
  so user --exclude patterns also filter content inside extracted
  archives, not only the source filesystem
- Add setupArchiveOrchestration() that creates the orchestrator when
  MaxDepth > 0, runs the iterative extract loop, and returns the
  composite resolver with archive contents visible to all catalogers
- Include archive config in configuration audit trail
- Add CLI archive config section (max-depth, size/file limits,
  exclude-extensions) wired through Catalog.ToArchiveConfig()
- CLI Catalog.ToSBOMConfig threads cfg.Exclusions through
  WithExclusions so child resolvers respect them

When MaxDepth is 0 (default), behavior is unchanged. When > 0, archives
discovered during scanning are extracted and their contents made visible
to all catalogers via the CompositeResolver.

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:49:00 +02:00
Thomas Bechtold 5d6ad0b9e8 test(archive): add edge case tests for Orchestrator
Add tests for important orchestrator edge cases:
- Nested archives (zip-inside-zip): verifies multi-round extraction
  discovers archives within previously-extracted archives
- Context cancellation: graceful handling without hanging
- Multiple archives at same level: both extracted in one round
- Corrupt archive: skipped without error, doesn't block other work
- Empty archive: not counted as extracted
- Per-archive file limit: partial extraction handled correctly
- Relationship structure: contains relationships emit the archive's
  coordinates as the source side

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:49:00 +02:00
Thomas Bechtold f7ea072900 feat(archive): add Orchestrator for iterative archive extraction
Implement the Orchestrator that manages the iterative
extract-catalog loop for recursive archive discovery:

- Scans the composite resolver for candidate archive locations,
  detecting format by peeking 512 bytes via FileContentsByLocation
  rather than copying every non-archive file to a probe-temp file
- Extracts identified archives to per-archive temp directories
- Builds child resolvers via an injectable ResolverFactory (kept
  pluggable so tests can substitute a mock resolver without
  importing syft/internal/fileresolver)
- Tracks extraction state to avoid reprocessing
- Enforces safety limits (depth, per-archive size, file count,
  total extraction budget)
- Generates ContainsRelationship entries linking each archive to
  its extracted-contents filesystem
- Manages temp directory lifecycle with Cleanup()

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:48:56 +02:00
Thomas Bechtold b26337759e test(archive): add edge case tests for CompositeResolver
Add tests for important edge cases:
- Nested children (child-in-child): verifies that archives within
  archives produce correct multi-level AccessPath chains like
  "/archive1.tar.gz:/inner-archive.zip:/deep-file.txt"
- Empty child resolver: no locations leak from empty archives
- Same file path in parent and child: both versions accessible
  with distinct FileSystemID routing
- Context cancellation in AllLocations: no hanging

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:47:22 +02:00
Thomas Bechtold 4240cb964f feat(archive): add CompositeResolver for transparent archive content access
Implement a file.Resolver decorator that composites a parent resolver
with child resolvers created from extracted archive contents. Files from
child resolvers appear transparently alongside parent files.

Key behaviors:
- FilesByPath/FilesByGlob/FilesByMIMEType delegate to parent AND children
- FileContentsByLocation routes to the correct resolver via FileSystemID
- Locations from children get archive-prefixed AccessPath (using ":"
  separator matching Java cataloger convention) and unique FileSystemID
- Thread-safe via RWMutex for concurrent access

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:47:22 +02:00
Thomas Bechtold 4df9a0ce10 feat(archive): add extractor interface and zip/tar implementations
Introduce the internal/archive package with a generic Extractor
interface for extracting archive contents to a destination directory.

Includes two implementations:
- ZipExtractor: handles zip-based archives (.zip, .jar, .war, etc.)
- TarExtractor: handles tar-based archives (.tar, .tar.gz, .tar.bz2, etc.)

Both extractors enforce configurable safety limits and protect against
common malicious-archive vectors:
- Per-archive extraction size limit
- Per-archive file count limit
- Zip-slip path traversal prevention via existing SafeJoin
- Symlink entries are written as real symlinks only when their target
  resolves inside destDir; symlinks pointing outside are dropped to
  avoid escape via symlink-then-write
- Non-regular non-symlink entries (devices, fifos, sockets) are skipped
  because their contents can't safely round-trip to disk

Also provides FindExtractor for auto-detecting archive format, and
IsExcludedExtension for filtering by extension.

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:47:16 +02:00
Thomas Bechtold 05b4cf1c84 feat(cataloging): extend ArchiveSearchConfig with recursive extraction settings
Add configuration fields for generic recursive archive extraction:
- MaxDepth: maximum nesting depth for archive extraction (default 0 = disabled)
- MaxExtractionSizeBytes: per-archive extraction size limit (default 500MB)
- MaxFileCount: per-archive file count limit (default 10,000)
- MaxTotalExtractionBytes: total extraction size limit (default 2GB)
- ExcludeExtensions: archive extensions to skip during extraction

These fields lay the groundwork for cross-cataloger recursive archive
cataloging, where archives discovered during scanning can be extracted
and their contents made visible to all catalogers.

Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
2026-04-27 08:46:09 +02:00
Kudryavcev Nikolay 8cfef40b9c fix realPath for first nested archives
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-07-02 01:00:02 +03:00
Kudryavcev Nikolay 8f784b5dd0 fix merge conflicts
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-06-29 21:11:29 +03:00
Kudryavcev Nikolay 5879cf9b10 Merge remote-tracking branch 'origin/main' into recursive-extract-archives 2025-06-29 21:04:02 +03:00
Kudryavcev Nikolay c8fbd613eb refactoring archive indexing
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-06-29 19:39:00 +03:00
Kudryavcev Nikolay 40df2d0d91 recursive indexing archives
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-06-29 17:06:59 +03:00
45 changed files with 228 additions and 1990 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ tools:
# used to upload test fixture cache
- name: oras
version:
want: v1.3.6
want: v1.3.4
method: github-release
with:
repo: oras-project/oras
+6 -1
View File
@@ -6,6 +6,10 @@ description: "Bootstrap all syft tools and dependencies on top of go-make's setu
# We add the syft-specific extras here: apt packages and the test fixture cache.
inputs:
go-version:
description: "Go version to install (passed to go-make/setup)"
required: true
default: "1.26.2"
cache-key-prefix:
description: "Prefix all cache keys with this value (passed to go-make/setup)"
required: true
@@ -26,8 +30,9 @@ runs:
using: "composite"
steps:
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@cf465259f4c9f0b0b0bcb35164ede11f7dc6d9f6 # v0.9.6
uses: anchore/go-make/.github/actions/setup@abf41688f36f2cbec13105747abbe8e60f5c56ca # v0.9.3
with:
go-version: ${{ inputs.go-version }}
cache-key-prefix: ${{ inputs.cache-key-prefix }}
cache-enabled: ${{ inputs.cache-enabled }}
+2 -2
View File
@@ -89,13 +89,13 @@ jobs:
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to Docker Hub
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 #v4.7.0
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f #v4.6.0
with:
username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }}
password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }}
- name: Login to GitHub Container Registry
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 #v4.7.0
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f #v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -25,7 +25,7 @@ jobs:
persist-credentials: false
- name: "Run zizmor"
uses: zizmorcore/zizmor-action@53290badfa369cb0bf7ad16342cb62c2fa47ac7b # v0.8.0
uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
with:
# there is a pass/fail gate as a repo ruleset (if there is no ruleset configured then the action will pass by default)
advanced-security: true
+12 -3
View File
@@ -99,7 +99,10 @@ jobs:
# go-make's setup only (not ./.github/actions/bootstrap): binny ships no windows release, so binny tools
# are unavailable here (`make unit` skips the fixture refresh on windows)
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@cf465259f4c9f0b0b0bcb35164ede11f7dc6d9f6 # v0.9.6
uses: anchore/go-make/.github/actions/setup@abf41688f36f2cbec13105747abbe8e60f5c56ca # v0.9.3
with:
# keep in sync with the go-version default in ./.github/actions/bootstrap
go-version: "1.26.2"
# the binny-free version of `make download-test-fixture-cache` (keep the oras version in sync with .binny.yaml)
- name: Download test fixture cache
@@ -192,7 +195,10 @@ jobs:
# go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@cf465259f4c9f0b0b0bcb35164ede11f7dc6d9f6 # v0.9.6
uses: anchore/go-make/.github/actions/setup@abf41688f36f2cbec13105747abbe8e60f5c56ca # v0.9.3
with:
# keep in sync with the go-version default in ./.github/actions/bootstrap
go-version: "1.26.2"
- name: Install Podman
run: |
@@ -222,7 +228,10 @@ jobs:
# go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@cf465259f4c9f0b0b0bcb35164ede11f7dc6d9f6 # v0.9.6
uses: anchore/go-make/.github/actions/setup@abf41688f36f2cbec13105747abbe8e60f5c56ca # v0.9.3
with:
# keep in sync with the go-version default in ./.github/actions/bootstrap
go-version: "1.26.2"
- name: Install Buildah
run: |
+4 -4
View File
@@ -2,11 +2,11 @@ module github.com/anchore/syft/.make
go 1.26.0
require github.com/anchore/go-make v0.9.6
require github.com/anchore/go-make v0.9.3
require (
github.com/bmatcuk/doublestar/v4 v4.10.3 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.2 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
golang.org/x/mod v0.42.0 // indirect
golang.org/x/sys v0.49.0 // indirect
golang.org/x/mod v0.41.0 // indirect
golang.org/x/sys v0.48.0 // indirect
)
+8 -8
View File
@@ -1,10 +1,10 @@
github.com/anchore/go-make v0.9.6 h1:HjO7H/M/WZ4v3B73/EuShuQHHblQJGwHDrl/MqCavWs=
github.com/anchore/go-make v0.9.6/go.mod h1:zE6KeB+nY+sMSWHYx2uaiOznLAq3CD9JvFlzWDDpKw4=
github.com/bmatcuk/doublestar/v4 v4.10.3 h1:1UIv1+fqH3++ViVkpnaPTB+Pi1UHycnPFAq14zgK+gI=
github.com/bmatcuk/doublestar/v4 v4.10.3/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/anchore/go-make v0.9.3 h1:D+MkpvEJsOGwkOqKN0rd2zYi9jEzwKtZreNysH0DenI=
github.com/anchore/go-make v0.9.3/go.mod h1:4HuELygGhxex2SaCEgfCbZ0geJ3xFkqtWpmt1NrYwPg=
github.com/bmatcuk/doublestar/v4 v4.10.2 h1:eF7W7HWKg3z9NrWV9pTLnNeoXaqq3Tq9DNKXVMfoCnw=
github.com/bmatcuk/doublestar/v4 v4.10.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
+36 -36
View File
@@ -12,7 +12,7 @@ require (
github.com/acobaugh/osrelease v0.1.0
github.com/adrg/xdg v0.5.3
github.com/anchore/bubbly v0.2.2
github.com/anchore/clio v0.1.2
github.com/anchore/clio v0.1.1
github.com/anchore/fangs v0.1.1
github.com/anchore/go-collections v0.1.1
github.com/anchore/go-homedir v0.1.1
@@ -22,28 +22,28 @@ require (
github.com/anchore/go-sync v0.1.2
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.2.0
github.com/anchore/stereoscope v0.4.1-0.20261103134938-3923a1d71f2e
github.com/anchore/stereoscope v0.3.4
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/bmatcuk/doublestar/v4 v4.10.3
github.com/bmatcuk/doublestar/v4 v4.10.2
github.com/cespare/xxhash/v2 v2.3.0
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v1.0.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/diskfs/go-diskfs v1.9.4
github.com/distribution/reference v0.6.0
github.com/dustin/go-humanize v1.1.0
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.8.0
github.com/gkampitakis/go-snaps v0.5.24
github.com/github/go-spdx/v2 v2.7.1
github.com/gkampitakis/go-snaps v0.5.23
github.com/go-git/go-billy/v5 v5.9.2
github.com/go-git/go-git/v5 v5.19.3
github.com/go-test/deep v1.1.2
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/goccy/go-yaml v1.19.2
github.com/gohugoio/hashstructure v0.6.0
@@ -51,8 +51,8 @@ require (
github.com/google/go-containerregistry v0.22.1
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.6.3
github.com/gpustack/gguf-parser-go v0.27.0
github.com/gookit/color v1.6.2
github.com/gpustack/gguf-parser-go v0.26.4
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-getter v1.8.10
github.com/hashicorp/go-multierror v1.1.1
@@ -62,7 +62,7 @@ require (
github.com/jedib0t/go-pretty/v6 v6.8.3
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/klauspost/compress v1.20.3
github.com/klauspost/compress v1.20.1
github.com/magiconair/properties v1.18.12
github.com/mholt/archives v0.1.5
github.com/moby/sys/mountinfo v0.7.2
@@ -93,11 +93,11 @@ require (
go.uber.org/goleak v1.3.0
go.yaml.in/yaml/v3 v3.0.5
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/mod v0.42.0
golang.org/x/mod v0.41.0
golang.org/x/net v0.61.0
golang.org/x/time v0.16.0
golang.org/x/tools v0.52.0
modernc.org/sqlite v1.61.0
golang.org/x/tools v0.51.0
modernc.org/sqlite v1.60.1
)
require (
@@ -123,9 +123,9 @@ require (
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0 // indirect
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // indirect
github.com/Microsoft/hcsshim v0.15.0-rc.4 // indirect
github.com/ProtonMail/go-crypto v1.5.2 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
@@ -156,6 +156,7 @@ require (
github.com/aws/smithy-go v1.28.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
@@ -172,7 +173,7 @@ require (
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
github.com/containerd/cgroups/v3 v3.1.3 // indirect
github.com/containerd/containerd/api v1.12.0 // indirect
github.com/containerd/containerd/v2 v2.4.2 // indirect
github.com/containerd/containerd/v2 v2.4.1 // indirect
github.com/containerd/continuity v0.5.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
@@ -181,16 +182,16 @@ require (
github.com/containerd/log/otel v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.5 // indirect
github.com/containerd/plugin v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.19.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/containerd/ttrpc v1.2.9 // indirect
github.com/containerd/typeurl/v2 v2.3.0 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/containers/ocicrypt v1.3.2 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
github.com/docker/cli v29.9.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.10 // indirect
github.com/docker/go-connections v0.8.2 // indirect
github.com/docker/cli v29.8.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.8 // indirect
github.com/docker/go-connections v0.8.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
@@ -201,16 +202,16 @@ require (
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.16 // indirect
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
github.com/gkampitakis/ciinfo v0.3.4 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-jose/go-jose/v4 v4.1.6 // indirect
github.com/go-jose/go-jose/v4 v4.1.5 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50 // indirect
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect
github.com/google/s2a-go v0.1.10 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.22 // indirect
github.com/googleapis/gax-go/v2 v2.24.1 // indirect
@@ -224,7 +225,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.7 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
@@ -232,8 +233,8 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.31 // indirect
github.com/mattn/go-sqlite3 v1.14.52 // indirect
github.com/mattn/go-runewidth v0.0.27 // indirect
github.com/mattn/go-sqlite3 v1.14.48 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/miekg/pkcs11 v1.1.2 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
@@ -245,8 +246,8 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/moby/api v1.56.1 // indirect
github.com/moby/moby/client v0.6.2 // indirect
github.com/moby/moby/api v1.56.0 // indirect
github.com/moby/moby/client v0.6.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.1 // indirect
@@ -256,7 +257,7 @@ require (
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v1.1.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nwaples/rardecode/v2 v2.4.1 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.2.0 // indirect
@@ -283,7 +284,7 @@ require (
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.11.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sigstore/fulcio v1.8.8 // indirect
github.com/sigstore/fulcio v1.8.7 // indirect
github.com/sigstore/protobuf-specs v0.5.2 // indirect
github.com/sigstore/sigstore v1.10.11 // indirect
github.com/sirupsen/logrus v1.10.2 // indirect
@@ -307,10 +308,9 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/varlink/go v0.4.1-0.20261015173642-c935be0c53f7 // indirect
github.com/vbatts/tar-split v0.12.3 // indirect
github.com/vbauerster/cupwriter v0.0.5 // indirect
github.com/vbauerster/mpb/v8 v8.16.2 // indirect
github.com/vbauerster/cupwriter v0.0.4 // indirect
github.com/vbauerster/mpb/v8 v8.14.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
@@ -327,8 +327,8 @@ require (
go.opentelemetry.io/otel/sdk v1.46.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect
go.opentelemetry.io/otel/trace v1.46.0 // indirect
go.podman.io/image/v5 v5.42.0 // indirect
go.podman.io/storage v1.65.0 // indirect
go.podman.io/image/v5 v5.41.2 // indirect
go.podman.io/storage v1.64.1 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.58.0 // indirect
golang.org/x/oauth2 v0.37.0 // indirect
@@ -344,7 +344,7 @@ require (
google.golang.org/grpc v1.84.0 // indirect
google.golang.org/protobuf v1.36.12 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.79.0 // indirect
modernc.org/libc v1.77.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.12.1 // indirect
)
+80 -80
View File
@@ -99,15 +99,15 @@ github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0 h1:71VU8kW/LCxJkx41+YYys1EgQO+AUnPa1TrxTSyYUCU=
github.com/Microsoft/go-winio v0.6.3-0.20260930231756-f19d9717deb0/go.mod h1:ma0QpxizD4fyyzJ1C7MIblymTD2Nxz5Mho/3CepwIZg=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
github.com/Microsoft/hcsshim v0.15.0-rc.4 h1:aZFX4LH0S20Lgjq0wG61StIClj7im4yzrxIClkaR8Z8=
github.com/Microsoft/hcsshim v0.15.0-rc.4/go.mod h1:BA9CBztgu4h/6Jsvo1O1M4qjWw09PoYpaEYgexPE578=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.5.2 h1:cucYnvqcY7UOXVD//mSyjeaPY0SSN3v5cDkYPxumINk=
github.com/ProtonMail/go-crypto v1.5.2/go.mod h1:/RaSu30DaKO4RY+XdV/ACcCcZkGr7AhUIduq5sjzzCo=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@@ -126,8 +126,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.2.2 h1:53JHRlpaNZjMcUYdz7n1U7+kOhy0zsJv9gpGEXvTcz8=
github.com/anchore/bubbly v0.2.2/go.mod h1:M4twoFkczlfuNdU8QV7oQjVJwPbxdUWRMfKydcLba0U=
github.com/anchore/clio v0.1.2 h1:kCWNUkJsRJ5A103fFSLG+CSqIP9bOIFX2X5GEnup0HM=
github.com/anchore/clio v0.1.2/go.mod h1:LqoM9TUMDolUMaPV4hslLvuooqIjpTN8J7V/rBqk/Zc=
github.com/anchore/clio v0.1.1 h1:9bkYnaFbpT0J3G0qSHyQqa/lpK5zV0C9Gtgh82H/jj0=
github.com/anchore/clio v0.1.1/go.mod h1:CFPIopJlakJbBpKtAL/EPpj4W9hgBO7PfFc8N2FAtlE=
github.com/anchore/fangs v0.1.1 h1:NBTHocmWM2/0gNhebpoE/wEB0Z0nxh2lESww2o/Uf5M=
github.com/anchore/fangs v0.1.1/go.mod h1:82oZuk4+RynOvAoxnXKmI1zO5xk/kbOw+5t4fAAPSsk=
github.com/anchore/go-collections v0.1.1 h1:kGlmSehs+ctTBZr+98F2+taHeolHy+XDx1Nczl8GZc8=
@@ -150,8 +150,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
github.com/anchore/stereoscope v0.4.1-0.20261103134938-3923a1d71f2e h1:5I/3QbnhqciUj2NOAMJTfzyyqR/e/JPtdNdhrPu5gEs=
github.com/anchore/stereoscope v0.4.1-0.20261103134938-3923a1d71f2e/go.mod h1:hYJsi3M8a8N1O66u+rMQSgv5TREGM7QIjKBjPPnwiD8=
github.com/anchore/stereoscope v0.3.4 h1:dT5AfjCTXUJowMiRIc+VBQVwvwnIEwnxxwA1GwAkMxw=
github.com/anchore/stereoscope v0.3.4/go.mod h1:+HxqHTt7dvH8mea0mf3ulqkf084VvHiT1grpAQf5uEs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
@@ -217,6 +217,8 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -227,8 +229,8 @@ github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAO
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/bmatcuk/doublestar/v4 v4.10.3 h1:1UIv1+fqH3++ViVkpnaPTB+Pi1UHycnPFAq14zgK+gI=
github.com/bmatcuk/doublestar/v4 v4.10.3/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.10.2 h1:eF7W7HWKg3z9NrWV9pTLnNeoXaqq3Tq9DNKXVMfoCnw=
github.com/bmatcuk/doublestar/v4 v4.10.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
@@ -298,8 +300,8 @@ github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/containerd/api v1.12.0 h1:kuQm82SbDrCuO4n7hf2L8zsBtZLuympyq5X/VotfX2A=
github.com/containerd/containerd/api v1.12.0/go.mod h1:EBcSzoi9Vl18cdODaXUCskf3D2NT8lsSXeZJnU5jIUc=
github.com/containerd/containerd/v2 v2.4.2 h1:p6Qg+xbEz8lvwdhHDx0U469xENvp4LgGBx+E7djXkGw=
github.com/containerd/containerd/v2 v2.4.2/go.mod h1:vgLdtvl3prFk1d3ZVqQcsDD5Q9IKM+vAIdU54U85w+I=
github.com/containerd/containerd/v2 v2.4.1 h1:DUx/ZJN7cEu0WuzHClDB+68H/bqMEH5pWoEjf0ae4hc=
github.com/containerd/containerd/v2 v2.4.1/go.mod h1:vgLdtvl3prFk1d3ZVqQcsDD5Q9IKM+vAIdU54U85w+I=
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -316,8 +318,8 @@ github.com/containerd/platforms v1.0.0-rc.5 h1:vXd569rDrz8LeMXzAnBsy6LADV5YtsD8o
github.com/containerd/platforms v1.0.0-rc.5/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U=
github.com/containerd/plugin v1.1.0/go.mod h1:qBTum+A8lJ6lO44A19Eo7y1OlcLj4OWFH1DA/vnHmcc=
github.com/containerd/stargz-snapshotter/estargz v0.19.0 h1:tak4qAx648jBAgZmywkd+6kaJ5pqSOVzKYB5DcVSHxk=
github.com/containerd/stargz-snapshotter/estargz v0.19.0/go.mod h1:Cr0pxOkuN7Y61+Sik6X6/nPtePW7hsKWdBt+MoHQRr0=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containerd/ttrpc v1.2.9 h1:ha0ak962T0s3CA/RoZ6S6xiWZQF24GrBaEpiGX1uihg=
github.com/containerd/ttrpc v1.2.9/go.mod h1:jjtQRwXm4DL3KsHKW8vDiUOV6wO0hi6IPhmJhxU7aEs=
github.com/containerd/typeurl/v2 v2.3.0 h1:HZHPhRWo5XMy3QGQoPrUzbW/2ckwjfweHmOwlkIrPAQ=
@@ -342,8 +344,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deitch/magic v1.0.0 h1:Nma1YAj7EB8Mlv4pVhbPpUp0CV1luYj2NOnE2+2xPNE=
github.com/deitch/magic v1.0.0/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/diskfs/go-diskfs v1.9.4 h1:0j2d7eG4IjyxL6+ChWbDPocdBCF6HQ4HBWU2WDYWVnc=
github.com/diskfs/go-diskfs v1.9.4/go.mod h1:TePJORO83Adh5pb2SqsxAwaP0fofFxKLkxctiS/9OQc=
@@ -351,14 +353,14 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/docker/cli v29.9.2+incompatible h1:trup/vUxx5uUo2t+87Cd9AmITR+DvKUe4H2tTtZN8FY=
github.com/docker/cli v29.9.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.8.2+incompatible h1:2zgdFuoFst2T80oS42vhKGxkd0JRo305eIEKTsxR7pQ=
github.com/docker/cli v29.8.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.10 h1:LCWwv7UmS7fQVmdKDWeMuureOeY5kkbC5kZfkzxkS6k=
github.com/docker/docker-credential-helpers v0.9.10/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.8.2 h1:9d9DEokx5W7qlr0shkFXIxEA7+TTD+llwzcykSlxMNc=
github.com/docker/go-connections v0.8.2/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q=
github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M=
github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
@@ -417,15 +419,15 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
github.com/gabriel-vasile/mimetype v1.4.16 h1:5izNFuvA37st1dDRAcA7TtmngBmRgiIPDmTtiEvjx1E=
github.com/gabriel-vasile/mimetype v1.4.16/go.mod h1:3oFFwxX8kTklZwzIl2aQEDZT0lKY8xH92ST88bAwba4=
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.8.0 h1:WKCnF2Ee/x+zwSIEeQdm6+be0H6PiCNLQmSZ5CFmxMI=
github.com/github/go-spdx/v2 v2.8.0/go.mod h1:Ftc45YYG1WzpzwEPKRVm9Jv8vDqOrN4gWoCkK+bHer0=
github.com/github/go-spdx/v2 v2.7.1 h1:+siV9wnsncI5pg4otH/sKvqGL8UkcLdPh16TSJYY1V4=
github.com/github/go-spdx/v2 v2.7.1/go.mod h1:Ftc45YYG1WzpzwEPKRVm9Jv8vDqOrN4gWoCkK+bHer0=
github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z0KY=
github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-snaps v0.5.24 h1:oyQ/CQA4s0nkVm6CLlM2GJSU8e4p6KGPZbrSCnTNBzE=
github.com/gkampitakis/go-snaps v0.5.24/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM=
github.com/gkampitakis/go-snaps v0.5.23 h1:okh5QR48zpUjpWtu65AtqxdCY8huJq+dEDuUzd1PuKg=
github.com/gkampitakis/go-snaps v0.5.23/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM=
github.com/glebarez/go-sqlite v1.23.0 h1:FyhIq4jqmgphQAUlY79zPldYGwISEZikaDfhiGWkkaI=
github.com/glebarez/go-sqlite v1.23.0/go.mod h1:IIYrOH3L0rHY3jb4IXOHoWdklNajSGUN2eJcvK8WrnI=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -441,8 +443,8 @@ github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.1.6 h1:sNRkftxFJxwsILiQukqp5z83QBSmntVwmgRY7AqBDO4=
github.com/go-jose/go-jose/v4 v4.1.6/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA=
github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -456,8 +458,8 @@ github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSr
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.1.2 h1:JUuyv17rKrqYfLFnP9A4Yh6hw0635ugB93bMg2bwuDQ=
github.com/go-test/deep v1.1.2/go.mod h1:lwVFiFFWPjydTsV3xGJ/YTQsnWwi8zxBFM+gOVcwINE=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@@ -553,8 +555,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50 h1:PlNWJRxu5APSV0pWqfIsZ0qt9OOGHSWfZQ4zvx+WgWs=
github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50/go.mod h1:yUo1AMMpsPIDFGuwDK8nLcf8+vvAd5l2O6JDZ6hpwrM=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.10 h1:EMp+aOuXN6l8cE/gjF5Bt+vyZxsUuyCWe9chDWR/+uU=
github.com/google/s2a-go v0.1.10/go.mod h1:pz4tyvwXvJLLbyrkh6FW1eS2zPUXMaTmyNhYtyP2tNw=
@@ -573,12 +575,12 @@ github.com/googleapis/gax-go/v2 v2.24.1/go.mod h1:bWeBei0NVwaNZKb2y1HUBS7gLXIF3/
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gookit/color v1.6.3 h1:m6E49x+KftGaspcLtPLMsyci2ARhWIUW0PUV1owMlhs=
github.com/gookit/color v1.6.3/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gookit/color v1.6.2 h1:M6TJRJMuDFPYkrWmjOvMj+E0xIjcHq8lMPy7+vv0z1Y=
github.com/gookit/color v1.6.2/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gpustack/gguf-parser-go v0.27.0 h1:eXFU9pmJEMQHOTL9qI742lXHWSa1CtlMWUU3gFce7nM=
github.com/gpustack/gguf-parser-go v0.27.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/gpustack/gguf-parser-go v0.26.4 h1:B9CRrQBcIB7I/MJFZvzPZ+e+cbc1xBzQJcmpi9l/mKU=
github.com/gpustack/gguf-parser-go v0.26.4/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.74 h1:mymLUKThnV9wFvogOK8NnsMP9/vlhnjXY98gr2QIGW8=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.74/go.mod h1:Bh9qYL8ehmDxSg14Tk8oxFCP90XHfs6NxV1D84884xA=
@@ -664,11 +666,11 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4=
github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.7 h1:02QB3Ttao6zOWDnSsv3bIvjN24bX0eGjWniQ8vuBfkA=
github.com/klauspost/pgzip v1.2.7/go.mod h1:g7E6NrOKHOzah4QwK6Ue1tNCJs8IDiNOfjiXTr85U2E=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -709,10 +711,10 @@ github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsRe
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.31 h1:1oCV4ReTlosDBc6rYRw5cL8S/4y8iv5oRl4SJ5IBgwo=
github.com/mattn/go-runewidth v0.0.31/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
github.com/mattn/go-sqlite3 v1.14.52 h1:wVbm2Qnf4OXkqhBTSPuCRZDRnxfbVrrmiCEroVdog8U=
github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0=
github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@@ -746,10 +748,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/moby/api v1.56.1 h1:PpWkvVPB7Fr/No8w+TfyJ/I6rWZ2YPZhT3JorIg+06c=
github.com/moby/moby/api v1.56.1/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk=
github.com/moby/moby/client v0.6.2 h1:ekSPlMccrZGcw6Scb8T9D39qE0mB+XupU/yS0zNw9N4=
github.com/moby/moby/client v0.6.2/go.mod h1:DGcnO7qmPJrqobx/ggQgQ/AUk+2wgVdQv7uxB0/mEsc=
github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ=
github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk=
github.com/moby/moby/client v0.6.0 h1:AJjEB21QPbXSXjDsZorFBoDZPhMrfbpaPLgSMAW9Bgs=
github.com/moby/moby/client v0.6.0/go.mod h1:OCo00wNRyA3m4lmJ228W3JbyCN4ZNNYjpOXiJydBdcQ=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
@@ -775,8 +777,8 @@ github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIf
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v1.1.0 h1:vtPmPWMkb539RGV9WpWT9EwJGvrOCKvMNI4obWL47Hk=
github.com/ncruces/go-strftime v1.1.0/go.mod h1:yVaq7iwCN09vHRrKBrrflXT0M9tTMG2KmZ9Fj7xeG/g=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+EDG4s40hMwE//d5SBgDjUOrITReV2u4aA=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
@@ -882,8 +884,8 @@ github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
@@ -899,8 +901,8 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sigstore/fulcio v1.8.8 h1:AtVoVQ4R7YaP6RHIH9puHyB398CHS/9WYc6d+gKf5Y0=
github.com/sigstore/fulcio v1.8.8/go.mod h1:RaFomAGGM7VZKAR9ZGqQhcf1Peryc9E1020/kbX8Uto=
github.com/sigstore/fulcio v1.8.7 h1:d7QJQxukgXarqPCT9uDNeyzt1Bbt/biw58/VfOnwI/o=
github.com/sigstore/fulcio v1.8.7/go.mod h1:7fX+QyigZxRGKTgHvTfOfxwwniCDxK02PVloHy5/vg4=
github.com/sigstore/protobuf-specs v0.5.2 h1:RSWWUY8QrVTxbYH00jY/jg2e7YnjzrpwP+PeHTMll0E=
github.com/sigstore/protobuf-specs v0.5.2/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats=
github.com/sigstore/sigstore v1.10.11 h1:fjrwDfR0AWmRONYV/hKGJyqbT0osMtaBz9FTZ6MH9XI=
@@ -994,16 +996,14 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.17 h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4=
github.com/ulikunitz/xz v0.5.17/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
github.com/varlink/go v0.4.1-0.20261015173642-c935be0c53f7 h1:pl46d1ZU17758S4EQhns0po6ZPtX+EdHgMPd1uvjia4=
github.com/varlink/go v0.4.1-0.20261015173642-c935be0c53f7/go.mod h1:Cqta0IQ13zfx9ZFugzjWDKUhyMryjOfMKoPrC3TY8fc=
github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E=
github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY=
github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw=
github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0=
github.com/vbauerster/cupwriter v0.0.5 h1:pxlPgxPLIebXh8lJPOZmn2TmjPE8+PqJIFkKkr7hI9I=
github.com/vbauerster/cupwriter v0.0.5/go.mod h1:ptJDgOtLtGZp1gLzycD0TFnZztDhrQdsikG6EAA4xYQ=
github.com/vbauerster/mpb/v8 v8.16.2 h1:+UH2Nhn6asKkdX58hSgN+5Jqg/JcFSqCcU+8z0IiYYo=
github.com/vbauerster/mpb/v8 v8.16.2/go.mod h1:PFkxR+jl7Eew9ukpdUcodHBGq9ow74b6cXI7xL0t3mA=
github.com/vbauerster/cupwriter v0.0.4 h1:9sBPe0uXWLZuWQU5lqVbhyFlxX6c09asST/YfatFAys=
github.com/vbauerster/cupwriter v0.0.4/go.mod h1:IFyzS6Xis5dnBH/rdAhrnuzg3c+KkUqEN6yE8lhJlDw=
github.com/vbauerster/mpb/v8 v8.14.0 h1:55SR80dptMfASxIG/oCEkBXgBhxeSu4GrVsjl16oKmA=
github.com/vbauerster/mpb/v8 v8.14.0/go.mod h1:HgpQPKfcWe3kbuGGPmi+jatHreMase5C3Fp5dpdAy0Q=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
@@ -1072,10 +1072,10 @@ go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwj
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.podman.io/image/v5 v5.42.0 h1:C//Ix0Thj2LYOQO6++NjU3McLFLQbf4czgUyH98R9gc=
go.podman.io/image/v5 v5.42.0/go.mod h1:mmIgf9EdpF68j8vdX1/OxhOSIknU1PO3G6/ClRkrwm4=
go.podman.io/storage v1.65.0 h1:GgY5VibEw6coVhOGYWuxZ4F7H2AaTfPiwL7E1CJ2dnM=
go.podman.io/storage v1.65.0/go.mod h1:ZDXsVSzdho+p4fDky+iMVCPwRtjS1kSiHcyhBwUB6M0=
go.podman.io/image/v5 v5.41.2 h1:YnVaFrGLPWSBmipKfNvlOsGcBtk5bUxlPxQ+A6CS/uo=
go.podman.io/image/v5 v5.41.2/go.mod h1:T/bzxEFpdpAzyHabPmcq5K+my3wBsht22sFkFjJSKT4=
go.podman.io/storage v1.64.1 h1:DqbG8scW/bD3xHiSRzV7O5fzn46qVC+SwmUN6C6Fr5w=
go.podman.io/storage v1.64.1/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
@@ -1147,8 +1147,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1410,8 +1410,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs=
golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs=
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1608,20 +1608,20 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
modernc.org/cc/v4 v4.30.3 h1:aDq8gIX1X7+VFgO/JYYji6pLWL2H4tv3g8WatT3KCys=
modernc.org/cc/v4 v4.30.3/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.37.0 h1:BAuJCUtIHIxFIYaFhqXIw5YaoMBlNCUlFovejHfMip4=
modernc.org/ccgo/v4 v4.37.0/go.mod h1:/8627VhwWtAbqUXmPORxFQU5SgLrAx2jl9seLw9GBhY=
modernc.org/cc/v4 v4.29.7 h1:q+NXGJ0bK3b4TXFYQQVr9pYETGnmwFWkrUzJnMya/Tg=
modernc.org/cc/v4 v4.29.7/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.36.1 h1:ZNIUZAryN0UgnJwtyxrdEzcFc3yD4Cu4AzjfPXsLsIE=
modernc.org/ccgo/v4 v4.36.1/go.mod h1:rrtGc2QkS239nYb/mQNuBMyjq3/y3ZXWbBjPoV3wqzA=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.2.0 h1:/Y3LGDMQaoYf55PebP1qsZ5FDGgAZyG1RYKEHeQzh1o=
modernc.org/gc/v3 v3.2.0/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.79.0 h1:9Dvvv++bAMSkSuBzuOFAatbTV5OIoq6Y6FohmD3e7q8=
modernc.org/libc v1.79.0/go.mod h1:uT3kR61NsbeKJSLqq83GJwSnZMVLlSjyAz4DfTaBy1M=
modernc.org/libc v1.77.1 h1:Ct8j47QtiZ1Enj2DtFXQtUqrPCAjdCmPjtCuvrYQ0Hs=
modernc.org/libc v1.77.1/go.mod h1:87/pZ4L6nD1zqW4nItuS12YO7hN1igAah34xjnQo/W0=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
@@ -1630,8 +1630,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.61.0 h1:sQi63oLQ9uquAUA4CzOisH35Q3pEszkPBuHM0tFL8CY=
modernc.org/sqlite v1.61.0/go.mod h1:f4gr5tbpx2umklaT/QVtbi/50hNdM+4ng+2zMo8KXJ0=
modernc.org/sqlite v1.60.1 h1:/blz53O951KWFOso4QQvEs/Fq6cDBKLtMVrYNSeJVKw=
modernc.org/sqlite v1.60.1/go.mod h1:1dIoEagfDE72QytD5scH1lxARtaUgKgHC/NuApA27r0=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+34 -34
View File
@@ -2131,40 +2131,40 @@ var urlToLicense = map[string]string{
"raw.githubusercontent.com/NTIA/scos-sensor/refs/heads/master/LICENSE.md": "NTIA-PD",
"raw.githubusercontent.com/OpenMDW/OpenMDW/refs/heads/main/1.0/LICENSE.openmdw": "OpenMDW-1.0",
"raw.githubusercontent.com/blackhole89/notekit/master/data/fonts/Charter%20license.txt": "Bitstream-Charter",
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/mdump.c": "Informatica",
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/msend.c": "Informatica",
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
"research.scea.com/scea_shared_source_license.html": "SCEA",
"romanrm.net/mit-zero": "MIT-0",
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
"scancode-licensedb.aboutcode.org/bsd-unchanged": "BSD-2-Clause-pos-unchanged",
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
"slicer.org/LICENSE": "3D-Slicer-1.0",
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/mdump.c": "Informatica",
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/msend.c": "Informatica",
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
"research.scea.com/scea_shared_source_license.html": "SCEA",
"romanrm.net/mit-zero": "MIT-0",
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
"scancode-licensedb.aboutcode.org/bsd-unchanged": "BSD-2-Clause-pos-unchanged",
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
"slicer.org/LICENSE": "3D-Slicer-1.0",
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=libiberty/strcasecmp.c;h=131d81c2ce7881fa48c363dc5bf5fb302c61ce0b;hb=HEAD": "BSD-4.3RENO",
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=readline/readline/support/wcwidth.c;h=0f5ec995796f4813abbcf4972aec0378ab74722a;hb=HEAD#l55": "HPND-Markus-Kuhn",
"sourceware.org/git/?p=bzip2.git;a=blob;f=LICENSE;hb=bzip2-1.0.6": "bzip2-1.0.6",
@@ -50,18 +50,6 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
}
}
// symlinks resolve to the same file, so drop repeated coordinates to avoid hashing the same contents more than once
seen := make(map[file.Coordinates]struct{}, len(locations))
unique := locations[:0]
for _, l := range locations {
if _, ok := seen[l.Coordinates]; ok {
continue
}
seen[l.Coordinates] = struct{}{}
unique = append(unique, l)
}
locations = unique
prog := catalogingProgress(ctx, int64(len(locations)))
err := sync.Collect(&ctx, cataloging.ExecutorFile, sync.ToSeq(locations), func(location file.Location) ([]file.Digest, error) {
@@ -7,7 +7,6 @@ import (
"io"
"os"
"path/filepath"
"sync"
"testing"
"github.com/stretchr/testify/assert"
@@ -208,45 +207,6 @@ func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
}
// countingResolver records how many times each coordinate's contents are opened
type countingResolver struct {
file.Resolver
lock sync.Mutex
counts map[file.Coordinates]int
}
func (r *countingResolver) FileContentsByLocation(l file.Location) (io.ReadCloser, error) {
r.lock.Lock()
r.counts[l.Coordinates]++
r.lock.Unlock()
return r.Resolver.FileContentsByLocation(l)
}
func TestDigestsCataloger_hashesEachCoordinateOnce(t *testing.T) {
testImage := "image-file-type-mix"
testutils.SkipWithoutLinuxContainers(t)
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
Reference: testImage,
})
resolver, err := src.FileResolver(source.SquashedScope)
require.NoError(t, err)
// /symlink-1 and /file-1.txt both resolve to the same coordinates
counter := &countingResolver{Resolver: resolver, counts: make(map[file.Coordinates]int)}
actual, err := NewCataloger([]crypto.Hash{crypto.MD5}).Catalog(context.Background(), counter)
require.NoError(t, err)
require.NotEmpty(t, actual)
for c, n := range counter.counts {
assert.Equal(t, 1, n, "contents of %+v opened %d times", c, n)
}
}
type panicReader struct{}
func (panicReader) Read([]byte) (int, error) { panic("boom") }
-39
View File
@@ -114,45 +114,6 @@ func TestCycloneDxImageEncoder(t *testing.T) {
)
}
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
dir := t.TempDir()
testutil.AssertEncoderAgainstGoldenSnapshot(t,
testutil.EncoderSnapshotTestConfig{
Subject: testutil.SourcePackagesInput(t, dir),
Format: getEncoder(t),
UpdateSnapshot: *updateSnapshot,
PersistRedactionsInSnapshot: true,
IsJSON: true,
Redactor: redactor(dir),
},
)
}
func TestCycloneDxSourcePackageEncoder_SpecVersions(t *testing.T) {
s := testutil.SourcePackagesInput(t, t.TempDir())
tests := []struct {
version string
want bool
}{
{version: "1.4", want: false},
{version: "1.5", want: false},
{version: "1.6", want: true},
{version: cyclonedxutil.DefaultVersion, want: true},
}
for _, tt := range tests {
t.Run(tt.version, func(t *testing.T) {
enc, err := NewFormatEncoderWithConfig(EncoderConfig{Version: tt.version})
require.NoError(t, err)
var buf bytes.Buffer
require.NoError(t, enc.Encode(&buf, s))
// the source-distribution reference type only exists from spec 1.6
assert.Equal(t, tt.want, strings.Contains(buf.String(), `"source-distribution"`))
})
}
}
func redactor(values ...string) testutil.Redactor {
return testutil.NewRedactions().
WithValuesRedacted(values...).
@@ -1,275 +0,0 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {
"timestamp": "timestamp:redacted",
"tools": {
"components": [
{
"type": "application",
"author": "anchore",
"name": "syft",
"version": "v0.42.0-bogus"
}
]
},
"component": {
"bom-ref": "redacted",
"type": "file",
"name": "some/path"
}
},
"components": [
{
"bom-ref": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&package-id=5cc0e8979d7f5115&upstream=attr%401%3A2.4.47-2",
"type": "library",
"name": "attr",
"version": "1:2.4.47-2+b1",
"purl": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
"externalReferences": [
{
"url": "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
"type": "source-distribution"
}
],
"properties": [
{
"name": "syft:package:foundBy",
"value": "dpkg-db-cataloger"
},
{
"name": "syft:package:type",
"value": "deb"
},
{
"name": "syft:package:metadataType",
"value": "dpkg-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/var/lib/dpkg/status"
},
{
"name": "syft:metadata:installedSize",
"value": "0"
},
{
"name": "syft:metadata:source",
"value": "attr"
},
{
"name": "syft:metadata:sourceVersion",
"value": "1:2.4.47-2"
}
]
},
{
"bom-ref": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&package-id=6008fca05ca7b418&upstream=bind-9.11.13-3.el8.src.rpm",
"type": "library",
"name": "bind-export-libs",
"version": "32:9.11.13-3.el8",
"purl": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
"externalReferences": [
{
"url": "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
"type": "source-distribution"
}
],
"properties": [
{
"name": "syft:package:foundBy",
"value": "rpm-db-cataloger"
},
{
"name": "syft:package:type",
"value": "rpm"
},
{
"name": "syft:package:metadataType",
"value": "rpm-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/var/lib/rpm/Packages"
},
{
"name": "syft:metadata:epoch",
"value": "32"
},
{
"name": "syft:metadata:release",
"value": "3.el8"
},
{
"name": "syft:metadata:size",
"value": "0"
},
{
"name": "syft:metadata:sourceRpm",
"value": "bind-9.11.13-3.el8.src.rpm"
}
]
},
{
"bom-ref": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3&package-id=aa74af1190de1c40",
"type": "library",
"name": "busybox",
"version": "1.35.0-r17",
"purl": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
"properties": [
{
"name": "syft:package:foundBy",
"value": "apk-db-cataloger"
},
{
"name": "syft:package:type",
"value": "apk"
},
{
"name": "syft:package:metadataType",
"value": "apk-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/lib/apk/db/installed"
},
{
"name": "syft:metadata:installedSize",
"value": "0"
},
{
"name": "syft:metadata:originPackage",
"value": "busybox"
},
{
"name": "syft:metadata:size",
"value": "0"
}
]
},
{
"bom-ref": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&package-id=58a2fcf749a9a3cc&upstream=gcc",
"type": "library",
"name": "gcc-libs",
"version": "13.2.1-3",
"purl": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
"properties": [
{
"name": "syft:package:foundBy",
"value": "alpm-db-cataloger"
},
{
"name": "syft:package:type",
"value": "alpm"
},
{
"name": "syft:package:metadataType",
"value": "alpm-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/var/lib/pacman/local/gcc-libs-13.2.1-3/desc"
},
{
"name": "syft:metadata:architecture",
"value": "x86_64"
},
{
"name": "syft:metadata:basepackage",
"value": "gcc"
},
{
"name": "syft:metadata:package",
"value": "gcc-libs"
},
{
"name": "syft:metadata:size",
"value": "0"
},
{
"name": "syft:metadata:version",
"value": "13.2.1-3"
}
]
},
{
"bom-ref": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&package-id=16a8c7b19fde6f4f&upstream=libc-dev",
"type": "library",
"name": "libc-utils",
"version": "0.7.2-r3",
"purl": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
"properties": [
{
"name": "syft:package:foundBy",
"value": "apk-db-cataloger"
},
{
"name": "syft:package:type",
"value": "apk"
},
{
"name": "syft:package:metadataType",
"value": "apk-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/lib/apk/db/installed"
},
{
"name": "syft:metadata:installedSize",
"value": "0"
},
{
"name": "syft:metadata:originPackage",
"value": "libc-dev"
},
{
"name": "syft:metadata:size",
"value": "0"
}
]
},
{
"bom-ref": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&package-id=7b4ddc97df57aec4&upstream=pam",
"type": "library",
"name": "libpam-runtime",
"version": "1.5.2-6+deb12u1",
"purl": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
"externalReferences": [
{
"url": "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
"type": "source-distribution"
}
],
"properties": [
{
"name": "syft:package:foundBy",
"value": "dpkg-db-cataloger"
},
{
"name": "syft:package:type",
"value": "deb"
},
{
"name": "syft:package:metadataType",
"value": "dpkg-db-entry"
},
{
"name": "syft:location:0:path",
"value": "/var/lib/dpkg/status"
},
{
"name": "syft:metadata:installedSize",
"value": "0"
},
{
"name": "syft:metadata:source",
"value": "pam"
}
]
}
]
}
-14
View File
@@ -87,20 +87,6 @@ func TestCycloneDxImageEncoder(t *testing.T) {
)
}
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
dir := t.TempDir()
testutil.AssertEncoderAgainstGoldenSnapshot(t,
testutil.EncoderSnapshotTestConfig{
Subject: testutil.SourcePackagesInput(t, dir),
Format: getEncoder(t),
UpdateSnapshot: *updateSnapshot,
PersistRedactionsInSnapshot: true,
IsJSON: false,
Redactor: redactor(dir),
},
)
}
func redactor(values ...string) testutil.Redactor {
return testutil.NewRedactions().
WithValuesRedacted(values...).
@@ -1,121 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>
<components>
<component type="application">
<author>anchore</author>
<name>syft</name>
<version>v0.42.0-bogus</version>
</component>
</components>
</tools>
<component bom-ref="redacted" type="file">
<name>some/path</name>
</component>
</metadata>
<components>
<component bom-ref="pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&amp;distro=debian-12&amp;package-id=5cc0e8979d7f5115&amp;upstream=attr%401%3A2.4.47-2" type="library">
<name>attr</name>
<version>1:2.4.47-2+b1</version>
<purl>pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&amp;distro=debian-12&amp;upstream=attr%401%3A2.4.47-2</purl>
<externalReferences>
<reference type="source-distribution">
<url>pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&amp;distro=debian-12</url>
</reference>
</externalReferences>
<properties>
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
<property name="syft:package:type">deb</property>
<property name="syft:package:metadataType">dpkg-db-entry</property>
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
<property name="syft:metadata:installedSize">0</property>
<property name="syft:metadata:source">attr</property>
<property name="syft:metadata:sourceVersion">1:2.4.47-2</property>
</properties>
</component>
<component bom-ref="pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&amp;distro=centos-8&amp;epoch=32&amp;package-id=6008fca05ca7b418&amp;upstream=bind-9.11.13-3.el8.src.rpm" type="library">
<name>bind-export-libs</name>
<version>32:9.11.13-3.el8</version>
<purl>pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&amp;distro=centos-8&amp;epoch=32&amp;upstream=bind-9.11.13-3.el8.src.rpm</purl>
<externalReferences>
<reference type="source-distribution">
<url>pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&amp;distro=centos-8&amp;epoch=32</url>
</reference>
</externalReferences>
<properties>
<property name="syft:package:foundBy">rpm-db-cataloger</property>
<property name="syft:package:type">rpm</property>
<property name="syft:package:metadataType">rpm-db-entry</property>
<property name="syft:location:0:path">/var/lib/rpm/Packages</property>
<property name="syft:metadata:epoch">32</property>
<property name="syft:metadata:release">3.el8</property>
<property name="syft:metadata:size">0</property>
<property name="syft:metadata:sourceRpm">bind-9.11.13-3.el8.src.rpm</property>
</properties>
</component>
<component bom-ref="pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&amp;distro=alpine-3.16.3&amp;package-id=aa74af1190de1c40" type="library">
<name>busybox</name>
<version>1.35.0-r17</version>
<purl>pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&amp;distro=alpine-3.16.3</purl>
<properties>
<property name="syft:package:foundBy">apk-db-cataloger</property>
<property name="syft:package:type">apk</property>
<property name="syft:package:metadataType">apk-db-entry</property>
<property name="syft:location:0:path">/lib/apk/db/installed</property>
<property name="syft:metadata:installedSize">0</property>
<property name="syft:metadata:originPackage">busybox</property>
<property name="syft:metadata:size">0</property>
</properties>
</component>
<component bom-ref="pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&amp;distro=arch-rolling&amp;package-id=58a2fcf749a9a3cc&amp;upstream=gcc" type="library">
<name>gcc-libs</name>
<version>13.2.1-3</version>
<purl>pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&amp;distro=arch-rolling&amp;upstream=gcc</purl>
<properties>
<property name="syft:package:foundBy">alpm-db-cataloger</property>
<property name="syft:package:type">alpm</property>
<property name="syft:package:metadataType">alpm-db-entry</property>
<property name="syft:location:0:path">/var/lib/pacman/local/gcc-libs-13.2.1-3/desc</property>
<property name="syft:metadata:architecture">x86_64</property>
<property name="syft:metadata:basepackage">gcc</property>
<property name="syft:metadata:package">gcc-libs</property>
<property name="syft:metadata:size">0</property>
<property name="syft:metadata:version">13.2.1-3</property>
</properties>
</component>
<component bom-ref="pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&amp;distro=alpine-3.16.3&amp;package-id=16a8c7b19fde6f4f&amp;upstream=libc-dev" type="library">
<name>libc-utils</name>
<version>0.7.2-r3</version>
<purl>pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&amp;distro=alpine-3.16.3&amp;upstream=libc-dev</purl>
<properties>
<property name="syft:package:foundBy">apk-db-cataloger</property>
<property name="syft:package:type">apk</property>
<property name="syft:package:metadataType">apk-db-entry</property>
<property name="syft:location:0:path">/lib/apk/db/installed</property>
<property name="syft:metadata:installedSize">0</property>
<property name="syft:metadata:originPackage">libc-dev</property>
<property name="syft:metadata:size">0</property>
</properties>
</component>
<component bom-ref="pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&amp;distro=debian-12&amp;package-id=7b4ddc97df57aec4&amp;upstream=pam" type="library">
<name>libpam-runtime</name>
<version>1.5.2-6+deb12u1</version>
<purl>pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&amp;distro=debian-12&amp;upstream=pam</purl>
<externalReferences>
<reference type="source-distribution">
<url>pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&amp;distro=debian-12</url>
</reference>
</externalReferences>
<properties>
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
<property name="syft:package:type">deb</property>
<property name="syft:package:metadataType">dpkg-db-entry</property>
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
<property name="syft:metadata:installedSize">0</property>
<property name="syft:metadata:source">pam</property>
</properties>
</component>
</components>
</bom>
@@ -2,7 +2,6 @@ package cyclonedxutil
import (
"io"
"slices"
"github.com/CycloneDX/cyclonedx-go"
@@ -30,35 +29,9 @@ func NewEncoder(version string, format cyclonedx.BOMFileFormat, pretty bool) (En
func (e Encoder) Encode(writer io.Writer, s sbom.SBOM) error {
bom := cyclonedxhelpers.ToFormatModel(s)
if e.version < cyclonedx.SpecVersion1_6 {
// cyclonedx-go does not downgrade this 1.6+ reference type for older spec versions
// (see https://github.com/CycloneDX/cyclonedx-go/issues/291), so drop it rather than emit an invalid document
dropExternalReferenceType(bom.Components, cyclonedx.ERTypeSourceDistribution)
}
enc := cyclonedx.NewBOMEncoder(writer, e.format)
enc.SetPretty(e.pretty)
enc.SetEscapeHTML(false)
return enc.EncodeVersion(bom, e.version)
}
func dropExternalReferenceType(components *[]cyclonedx.Component, refType cyclonedx.ExternalReferenceType) {
if components == nil {
return
}
for i := range *components {
c := &(*components)[i]
dropExternalReferenceType(c.Components, refType)
if c.ExternalReferences == nil {
continue
}
refs := slices.DeleteFunc(*c.ExternalReferences, func(r cyclonedx.ExternalReference) bool {
return r.Type == refType
})
if len(refs) == 0 {
c.ExternalReferences = nil
continue
}
c.ExternalReferences = &refs
}
}
@@ -345,73 +345,6 @@ func Test_decodeComponent(t *testing.T) {
},
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
},
{
// regression: the self-referencing Parts field made decoding recurse without bound
name: "decode safetensors model metadata",
component: cyclonedx.Component{
Name: "lib",
Version: "UNKNOWN",
Type: cyclonedx.ComponentTypeMachineLearningModel,
Properties: &[]cyclonedx.Property{
{Name: "syft:package:foundBy", Value: "safetensors-cataloger"},
{Name: "syft:package:type", Value: "model"},
{Name: "syft:package:metadataType", Value: "safetensors-model-info"},
{Name: "syft:metadata:format", Value: "safetensors"},
{Name: "syft:metadata:metadataHash", Value: "afcae99661c33f88"},
{Name: "syft:metadata:parameters", Value: "16"},
{Name: "syft:metadata:quantization", Value: "I8"},
{Name: "syft:metadata:shardCount", Value: "1"},
{Name: "syft:metadata:tensorCount", Value: "1"},
},
},
wantMetadata: pkg.SafeTensorsModelInfo{
Format: "safetensors",
MetadataHash: "afcae99661c33f88",
Parameters: 16,
Quantization: "I8",
ShardCount: 1,
TensorCount: 1,
},
},
{
name: "decode safetensors model metadata with parts",
component: cyclonedx.Component{
Name: "model",
Type: cyclonedx.ComponentTypeMachineLearningModel,
Properties: &[]cyclonedx.Property{
{Name: "syft:package:type", Value: "model"},
{Name: "syft:package:metadataType", Value: "safetensors-model-info"},
{Name: "syft:metadata:format", Value: "safetensors"},
{Name: "syft:metadata:parts:0:format", Value: "safetensors"},
{Name: "syft:metadata:parts:0:tensorCount", Value: "2"},
{Name: "syft:metadata:parts:1:tensorCount", Value: "3"},
},
},
wantMetadata: pkg.SafeTensorsModelInfo{
Format: "safetensors",
Parts: []pkg.SafeTensorsModelInfo{
{Format: "safetensors", TensorCount: 2},
{TensorCount: 3},
},
},
},
{
name: "decode gguf model metadata",
component: cyclonedx.Component{
Name: "model",
Type: cyclonedx.ComponentTypeMachineLearningModel,
Properties: &[]cyclonedx.Property{
{Name: "syft:package:type", Value: "model"},
{Name: "syft:package:metadataType", Value: "gguf-file-header"},
{Name: "syft:metadata:ggufVersion", Value: "3"},
{Name: "syft:metadata:tensorCount", Value: "291"},
},
},
wantMetadata: pkg.GGUFFileHeader{
GGUFVersion: 3,
TensorCount: 291,
},
},
{
name: "generate a purl from package type",
component: cyclonedx.Component{
@@ -7,7 +7,6 @@ import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/file"
syftFile "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -80,162 +79,12 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
}
}
}
if srcRef := encodeSourcePackageExternalReference(p); srcRef != nil {
refs = append(refs, *srcRef)
}
if len(refs) > 0 {
return &refs
}
return nil
}
// encodeSourcePackageExternalReference returns a "source-distribution" reference holding the PURL of the distro
// source package that a binary OS package was built from (e.g. libpam-runtime -> pkg:deb/debian/pam@...?arch=source).
// It is only emitted when the source differs from the binary package (by name or version) and when the package PURL
// is of the type implied by the metadata. The main consumer is vulnerability matching, since distros tend to report
// vulnerabilities against the source package rather than each binary package built from it.
//
// why an external reference and not pedigree: `component.pedigree.ancestors` looks like the obvious spot for "this
// binary came from that source", but pedigree describes code lineage (forks, patched variants, and the patches that
// separate them). A binary built unmodified from its own distro source package is the same code in a different form,
// not a fork, and CycloneDX components are deliberately agnostic to source vs binary form (see the comments from the
// CycloneDX maintainers in https://github.com/CycloneDX/specification/issues/612#issuecomment-2958800363). The
// ancestor slot is better left for the upstream project a distro patched (e.g. Linux-PAM for Debian's pam), which
// syft does not know today.
//
// why "source-distribution": the spec defines it as "the location where the source code distributable can be
// obtained", and a deb/rpm source package is exactly that distributable (a .dsc and tarballs in the Debian source
// pool, a .src.rpm in an SRPMS repo). The value is a PURL (an identifier) rather than a download URL, which the
// schema allows since external reference URLs are URIs of any scheme, and a CycloneDX maintainer confirmed this exact
// shape is valid in https://github.com/CycloneDX/specification/issues/612#issuecomment-5428057874. A first-class
// "source" component type was discussed in that issue and deferred to CycloneDX 2.0.
func encodeSourcePackageExternalReference(p pkg.Package) *cyclonedx.ExternalReference {
if p.PURL == "" {
return nil
}
src := sourcePackageOf(p)
if src == nil {
return nil
}
// the binary PURL is only consulted for the namespace and a small set of qualifiers (e.g. distro), everything
// else describes the binary package and must not be restated as a fact about the source package.
binPurl, err := packageurl.FromString(p.PURL)
if err != nil || binPurl.Type != src.purlType {
return nil
}
qualifiers := map[string]string{}
for _, q := range binPurl.Qualifiers {
if src.keepQualifier(q.Key) {
qualifiers[q.Key] = q.Value
}
}
if src.arch != "" {
qualifiers[pkg.PURLQualifierArch] = src.arch
}
srcPurl := packageurl.NewPackageURL(
src.purlType,
binPurl.Namespace,
src.name,
src.version,
packageurl.QualifiersFromMap(qualifiers),
"",
)
return &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution,
URL: srcPurl.ToString(),
}
}
// sourcePackage is the identity of the source package a binary OS package was built from.
type sourcePackage struct {
purlType string
name string
version string
// arch is the ecosystem's source architecture marker ("source" for deb, "src" or "nosrc" for rpm)
arch string
}
func (s sourcePackage) keepQualifier(key string) bool {
switch key {
case pkg.PURLQualifierDistro:
return true
case pkg.PURLQualifierEpoch, pkg.PURLQualifierRpmModularity:
return s.purlType == pkg.RpmPkg.PackageURLType()
}
return false
}
// sourcePackageOf returns the source package for p, or nil when there is none or it is the same as the binary.
//
// only deb and rpm are supported, since those are the ecosystems that publish real source packages with their own
// PURL identity (arch=source / arch=src). apk (origin) and alpm (pkgbase) are intentionally left out: there the
// "source" is a build recipe rather than a published source distributable, and its name is usually also the name of
// a real binary package (e.g. pkg:apk/alpine/libc-dev@0.7.2-r3 is an installable package, not a source archive), so
// a "source-distribution" reference would point at a binary package. Whether those ecosystems have a meaningful
// source PURL is still an open question, and until then the `upstream` PURL qualifier continues to carry that info.
func sourcePackageOf(p pkg.Package) *sourcePackage {
var src sourcePackage
var binName, binVersion string
switch m := p.Metadata.(type) {
case pkg.DpkgDBEntry:
src = dpkgSourcePackage(m)
binName, binVersion = m.Package, m.Version
case pkg.DpkgArchiveEntry:
src = dpkgSourcePackage(pkg.DpkgDBEntry(m))
binName, binVersion = m.Package, m.Version
case pkg.RpmDBEntry:
src = rpmSourcePackage(m.SourceRpm)
binName, binVersion = m.Name, m.Version+"-"+m.Release
case pkg.RpmArchive:
src = rpmSourcePackage(m.SourceRpm)
binName, binVersion = m.Name, m.Version+"-"+m.Release
default:
return nil
}
if src.name == "" || src.version == "" {
return nil
}
if src.name == binName && src.version == binVersion {
return nil
}
return &src
}
func dpkgSourcePackage(entry pkg.DpkgDBEntry) sourcePackage {
version := entry.SourceVersion
if version == "" {
version = entry.Version
}
return sourcePackage{purlType: pkg.DebPkg.PackageURLType(), name: entry.Source, version: version, arch: "source"}
}
// rpmSourcePackage parses a source RPM filename of the form <name>-<version>-<release>.(no)src.rpm.
func rpmSourcePackage(sourceRpm string) sourcePackage {
var arch string
switch {
case strings.HasSuffix(sourceRpm, ".src.rpm"):
arch = "src"
case strings.HasSuffix(sourceRpm, ".nosrc.rpm"):
arch = "nosrc"
default:
return sourcePackage{}
}
nvr := strings.TrimSuffix(sourceRpm, "."+arch+".rpm")
release := strings.LastIndex(nvr, "-")
if release < 0 {
return sourcePackage{}
}
ver := strings.LastIndex(nvr[:release], "-")
if ver < 0 {
return sourcePackage{}
}
return sourcePackage{purlType: pkg.RpmPkg.PackageURLType(), name: nvr[:ver], version: nvr[ver+1:], arch: arch}
}
// supported algorithm in cycloneDX as of 1.4
// "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512",
// "SHA3-256", "SHA3-384", "SHA3-512", "BLAKE2b-256", "BLAKE2b-384", "BLAKE2b-512", "BLAKE3"
@@ -144,342 +144,6 @@ func Test_encodeExternalReferences(t *testing.T) {
}
}
func Test_encodeSourcePackage(t *testing.T) {
rpmEpoch := 32
tests := []struct {
name string
input pkg.Package
expected *cyclonedx.ExternalReference
}{
{
name: "no metadata",
input: pkg.Package{},
expected: nil,
},
{
name: "no PURL",
input: pkg.Package{
Name: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Package: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Source: "pam",
},
},
expected: nil,
},
{
// apk origins are build recipes, not published source packages, so they are not supported
name: "from apk is not supported",
input: pkg.Package{
Name: "libc-utils",
Version: "0.7.2-r3",
Type: pkg.ApkPkg,
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
Metadata: pkg.ApkDBEntry{
Package: "libc-utils",
OriginPackage: "libc-dev",
Version: "0.7.2-r3",
Architecture: "x86_64",
},
},
expected: nil,
},
{
name: "from deb",
input: pkg.Package{
Name: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
Metadata: pkg.DpkgDBEntry{
Package: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Source: "pam",
Architecture: "all",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
},
},
{
name: "from deb with source version",
input: pkg.Package{
Name: "attr",
Version: "1:2.4.47-2+b1",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
Metadata: pkg.DpkgDBEntry{
Package: "attr",
Version: "1:2.4.47-2+b1",
Source: "attr",
SourceVersion: "1:2.4.47-2",
Architecture: "amd64",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
},
},
{
name: "from deb with empty source",
input: pkg.Package{
Name: "some-package",
Version: "1.0.0",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/some-package@1.0.0?arch=all&distro=debian-12",
Metadata: pkg.DpkgDBEntry{
Package: "some-package",
Version: "1.0.0",
Architecture: "all",
},
},
expected: nil,
},
{
name: "from deb archive",
input: pkg.Package{
Name: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Type: pkg.DebPkg,
PURL: "pkg:deb/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&upstream=pam",
Metadata: pkg.DpkgArchiveEntry{
Package: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Source: "pam",
Architecture: "all",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/pam@1.5.2-6%2Bdeb12u1?arch=source",
},
},
{
name: "from rpm",
input: pkg.Package{
Name: "bind-export-libs",
Version: "32:9.11.13-3.el8",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
Metadata: pkg.RpmDBEntry{
Name: "bind-export-libs",
Version: "9.11.13",
Release: "3.el8",
Epoch: &rpmEpoch,
Arch: "x86_64",
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
},
},
{
name: "from rpm with source = bin",
input: pkg.Package{
Name: "some-package",
Version: "32:1.0.0-1.el8",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/centos/some-package@1.0.0-1.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=some-package-1.0.0-1.el8.src.rpm",
Metadata: pkg.RpmDBEntry{
Name: "some-package",
Version: "1.0.0",
Release: "1.el8",
Epoch: &rpmEpoch,
Arch: "x86_64",
SourceRpm: "some-package-1.0.0-1.el8.src.rpm",
},
},
expected: nil,
},
{
name: "from rpm archive",
input: pkg.Package{
Name: "bind-export-libs",
Version: "32:9.11.13-3.el8",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/bind-export-libs@9.11.13-3.el8?arch=x86_64&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
Metadata: pkg.RpmArchive{
Name: "bind-export-libs",
Version: "9.11.13",
Release: "3.el8",
Epoch: &rpmEpoch,
Arch: "x86_64",
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/bind@9.11.13-3.el8?arch=src&epoch=32",
},
},
{
// alpm pkgbases are build recipes, not published source packages, so they are not supported
name: "from alpm is not supported",
input: pkg.Package{
Name: "gcc-libs",
Version: "13.2.1-3",
Type: pkg.AlpmPkg,
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
Metadata: pkg.AlpmDBEntry{
Package: "gcc-libs",
Version: "13.2.1-3",
BasePackage: "gcc",
Architecture: "x86_64",
},
},
expected: nil,
},
{
name: "from rpm with dashes in the source name",
input: pkg.Package{
Name: "perl-Foo-Bar-libs",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/perl-Foo-Bar-libs@1.0-1.el8?arch=x86_64&distro=rhel-8",
Metadata: pkg.RpmDBEntry{
Name: "perl-Foo-Bar-libs",
Version: "1.0",
Release: "1.el8",
SourceRpm: "perl-Foo-Bar-1.0-1.el8.src.rpm",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/perl-Foo-Bar@1.0-1.el8?arch=src&distro=rhel-8",
},
},
{
name: "from rpm nosrc",
input: pkg.Package{
Name: "some-firmware",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/some-firmware@1.0-1.el8?arch=noarch&distro=rhel-8",
Metadata: pkg.RpmDBEntry{
Name: "some-firmware",
Version: "1.0",
Release: "1.el8",
SourceRpm: "firmware-1.0-1.el8.nosrc.rpm",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/firmware@1.0-1.el8?arch=nosrc&distro=rhel-8",
},
},
{
name: "from rpm keeps rpmmod",
input: pkg.Package{
Name: "nodejs-libs",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/nodejs-libs@18.0-1.el8?arch=x86_64&distro=rhel-8&rpmmod=nodejs%3A18",
Metadata: pkg.RpmDBEntry{
Name: "nodejs-libs",
Version: "18.0",
Release: "1.el8",
SourceRpm: "nodejs-18.0-1.el8.src.rpm",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/nodejs@18.0-1.el8?arch=src&distro=rhel-8&rpmmod=nodejs%3A18",
},
},
{
name: "from rpm with malformed source rpm",
input: pkg.Package{
Name: "gpg-pubkey",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/gpg-pubkey@1-1?distro=rhel-8",
Metadata: pkg.RpmDBEntry{
Name: "gpg-pubkey",
Version: "1",
Release: "1",
SourceRpm: "(none)",
},
},
expected: nil,
},
{
name: "from rpm with non-source rpm filename",
input: pkg.Package{
Name: "bash",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
Metadata: pkg.RpmDBEntry{
Name: "bash",
Version: "5.1",
Release: "2",
SourceRpm: "bash-5.1-2.x86_64.rpm",
},
},
expected: nil,
},
{
name: "from rpm with too few dashes",
input: pkg.Package{
Name: "bash",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
Metadata: pkg.RpmDBEntry{
Name: "bash",
Version: "5.1",
Release: "2",
SourceRpm: "bash-5.1.src.rpm",
},
},
expected: nil,
},
{
name: "binary qualifiers and subpath are not carried over",
input: pkg.Package{
Name: "libssl3",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/libssl3@3.0.0-1?arch=amd64&distro=debian-12&checksum=sha256:dead&download_url=https://example.com/x.deb&cpes=cpe:2.3:a:x:x:1:*:*:*:*:*:*:*#sub/dir",
Metadata: pkg.DpkgDBEntry{
Package: "libssl3",
Version: "3.0.0-1",
Source: "openssl",
},
},
expected: &cyclonedx.ExternalReference{
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/openssl@3.0.0-1?arch=source&distro=debian-12",
},
},
{
name: "PURL type does not match metadata",
input: pkg.Package{
Name: "x",
Type: pkg.DebPkg,
PURL: "pkg:npm/x@1",
Metadata: pkg.DpkgDBEntry{
Package: "x",
Version: "1",
Source: "lodash",
},
},
expected: nil,
},
{
name: "invalid PURL",
input: pkg.Package{
Name: "x",
Type: pkg.DebPkg,
PURL: "not-a-purl",
Metadata: pkg.DpkgDBEntry{
Package: "x",
Version: "1",
Source: "y",
},
},
expected: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, encodeSourcePackageExternalReference(test.input))
})
}
}
func Test_isValidExternalRef(t *testing.T) {
tests := []struct {
name string
@@ -171,7 +171,7 @@ func Decode(typ reflect.Type, values map[string]string, prefix string, fn FieldN
v := reflect.New(typ)
decode(values, sortedPropertyNames(values), v, prefix, fn)
decode(values, v, prefix, fn)
switch {
case isSlice && isPtr:
@@ -192,11 +192,11 @@ func DecodeInto(obj any, values map[string]string, prefix string, fn FieldName)
value = value.Elem()
}
decode(values, sortedPropertyNames(values), value, prefix, fn)
decode(values, value, prefix, fn)
}
//nolint:funlen,gocognit,gocyclo
func decode(vals map[string]string, names propertyNames, value reflect.Value, prefix string, fn FieldName) bool {
func decode(vals map[string]string, value reflect.Value, prefix string, fn FieldName) bool {
if !value.IsValid() {
return false
}
@@ -211,7 +211,7 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
if v.IsNil() {
v = reflect.New(t)
}
if decode(vals, names, v.Elem(), prefix, fn) && value.CanSet() {
if decode(vals, v.Elem(), prefix, fn) && value.CanSet() {
o := v.Interface()
log.Tracef("%v", o)
value.Set(v)
@@ -273,7 +273,7 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
newType = t.Elem()
}
v := reflect.New(newType)
if decode(vals, names, v.Elem(), str, fn) {
if decode(vals, v.Elem(), str, fn) {
// append to slice
if t.Kind() != reflect.Pointer {
v = v.Elem()
@@ -308,8 +308,7 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
newKeyType = keyType.Elem()
}
k := reflect.New(newKeyType)
// a key is decoded from the single property holding it, so it has no nested property names
if !decode(keyVals, nil, k.Elem(), key, fn) {
if !decode(keyVals, k.Elem(), key, fn) {
log.Debugf("unable to decode key for: %s", key)
continue
}
@@ -323,7 +322,7 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
newValueType = valueType.Elem()
}
v := reflect.New(newValueType)
if decode(vals, names, v.Elem(), key, fn) {
if decode(vals, v.Elem(), key, fn) {
if valueType.Kind() != reflect.Pointer {
v = v.Elem()
}
@@ -340,13 +339,6 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
return false
}
case reflect.Struct:
// every field is decoded from the property named prefix or from one nested below it, so there is
// nothing to decode when there is none. Stopping here also bounds the recursion for a type that refers
// to itself (e.g. a Parts []T field): otherwise decoding the first element of an absent []T decodes
// the same []T one level deeper, without end.
if !names.hasPrefix(prefix) {
return false
}
values := false
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
@@ -357,7 +349,7 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
continue
}
if decode(vals, names, v, name, fn) {
if decode(vals, v, name, fn) {
values = true
}
}
@@ -369,34 +361,6 @@ func decode(vals map[string]string, names propertyNames, value reflect.Value, pr
return true
}
// propertyNames holds the names of the properties being decoded, sorted, so that decode can find whether a property
// is nested below a prefix without scanning every property for every struct it decodes
type propertyNames []string
func sortedPropertyNames(vals map[string]string) propertyNames {
names := make(propertyNames, 0, len(vals))
for name := range vals {
names = append(names, name)
}
sort.Strings(names)
return names
}
// hasPrefix reports whether there is a property named prefix or any property nested below it
func (names propertyNames) hasPrefix(prefix string) bool {
if prefix == "" {
return len(names) > 0
}
i := sort.SearchStrings(names, prefix)
if i < len(names) && names[i] == prefix {
return true
}
// names sorting between prefix and prefix+":" (e.g. "parts:10" between "parts:1" and "parts:1:") are skipped
nested := prefix + ":"
i += sort.SearchStrings(names[i:], nested)
return i < len(names) && strings.HasPrefix(names[i], nested)
}
func PtrToStruct(ptr any) any {
v := reflect.ValueOf(ptr)
if v.IsZero() && v.Type().Kind() != reflect.Struct {
@@ -41,13 +41,6 @@ type T5 struct {
Map map[string]string
}
// TRecursive refers to itself, like pkg.SafeTensorsModelInfo and pkg.GGUFFileHeader do through their Parts field
type TRecursive struct {
Name string
Children []TRecursive
Next *TRecursive
}
func Test_EncodeDecodeCycle(t *testing.T) {
val := 99
@@ -129,33 +122,6 @@ func Test_EncodeDecodeCycle(t *testing.T) {
{"t2 elem 1"},
},
},
{
name: "recursive type without children",
value: &TRecursive{
Name: "root",
},
},
{
name: "recursive type with nested children",
value: &TRecursive{
Name: "root",
Children: []TRecursive{
{
Name: "child 0",
Children: []TRecursive{
{Name: "grandchild 0"},
},
},
{
Name: "child 1",
Next: &TRecursive{Name: "next of child 1"},
},
},
Next: &TRecursive{
Name: "next",
},
},
},
{
name: "map of strings",
value: &T5{
@@ -188,56 +154,3 @@ func Test_EncodeDecodeCycle(t *testing.T) {
})
}
}
func Test_propertyNames_hasPrefix(t *testing.T) {
names := sortedPropertyNames(map[string]string{
"props:name": "root",
"props:children:10:x": "a",
"props:children:2:x": "b",
"props:children:3": "c",
"props:children:40": "d",
"props:children:4:x": "e",
})
tests := []struct {
prefix string
want bool
}{
{prefix: "", want: true},
{prefix: "props", want: true},
{prefix: "props:name", want: true},
{prefix: "props:nam", want: false},
{prefix: "props:children", want: true},
{prefix: "props:children:0", want: false},
// only "props:children:10:x" shares this prefix as a string
{prefix: "props:children:1", want: false},
{prefix: "props:children:10", want: true},
{prefix: "props:children:2", want: true},
{prefix: "props:children:3", want: true},
{prefix: "props:children:3:x", want: false},
// "props:children:40" sorts between this prefix and the nested "props:children:4:x"
{prefix: "props:children:4", want: true},
{prefix: "other", want: false},
}
for _, test := range tests {
t.Run(test.prefix, func(t *testing.T) {
assert.Equal(t, test.want, names.hasPrefix(test.prefix))
})
}
assert.False(t, sortedPropertyNames(nil).hasPrefix(""))
}
func Test_DecodeRecursiveTypeWithManyChildren(t *testing.T) {
// more than ten children, so that "children:1" sorts next to "children:10"
value := &TRecursive{Name: "root"}
for i := 0; i < 12; i++ {
value.Children = append(value.Children, TRecursive{
Children: []TRecursive{{Name: "grandchild"}},
})
}
vals := Encode(value, "props", OptionalJSONTag)
assert.EqualValues(t, value, Decode(reflect.TypeOf(value), vals, "props", OptionalJSONTag))
}
@@ -1,146 +0,0 @@
package testutil
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source/directorysource"
)
func SourcePackagesInput(t testing.TB, dir string) sbom.SBOM {
path := filepath.Join(dir, "some", "path")
require.NoError(t, os.MkdirAll(path, 0755))
src, err := directorysource.New(
directorysource.Config{
Path: path,
Base: dir,
},
)
require.NoError(t, err)
return sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: newSourcePackageCatalog(),
},
Source: src.Describe(),
Descriptor: sbom.Descriptor{
Name: "syft",
Version: "v0.42.0-bogus",
},
}
}
//nolint:funlen
func newSourcePackageCatalog() *pkg.Collection {
epoch := 32
catalog := pkg.NewCollection()
// apk: the origin differs from the package name, but apk is not supported so no source reference is expected
catalog.Add(pkg.Package{
Name: "libc-utils",
Version: "0.7.2-r3",
Type: pkg.ApkPkg,
FoundBy: "apk-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
Metadata: pkg.ApkDBEntry{
Package: "libc-utils",
OriginPackage: "libc-dev",
Version: "0.7.2-r3",
Architecture: "x86_64",
},
})
// apk: negative control -- the package is its own origin
catalog.Add(pkg.Package{
Name: "busybox",
Version: "1.35.0-r17",
Type: pkg.ApkPkg,
FoundBy: "apk-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
Metadata: pkg.ApkDBEntry{
Package: "busybox",
OriginPackage: "busybox",
Version: "1.35.0-r17",
Architecture: "x86_64",
},
})
// deb: source name differs, and the source version is implied by the binary version
catalog.Add(pkg.Package{
Name: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Type: pkg.DebPkg,
FoundBy: "dpkg-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
Metadata: pkg.DpkgDBEntry{
Package: "libpam-runtime",
Version: "1.5.2-6+deb12u1",
Source: "pam",
Architecture: "all",
},
})
// deb: the source version differs from the binary version
catalog.Add(pkg.Package{
Name: "attr",
Version: "1:2.4.47-2+b1",
Type: pkg.DebPkg,
FoundBy: "dpkg-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
Metadata: pkg.DpkgDBEntry{
Package: "attr",
Version: "1:2.4.47-2+b1",
Source: "attr",
SourceVersion: "1:2.4.47-2",
Architecture: "amd64",
},
})
// rpm: name and version both come out of the source RPM filename, and the epoch is carried over
catalog.Add(pkg.Package{
Name: "bind-export-libs",
Version: "32:9.11.13-3.el8",
Type: pkg.RpmPkg,
FoundBy: "rpm-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/var/lib/rpm/Packages")),
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
Metadata: pkg.RpmDBEntry{
Name: "bind-export-libs",
Version: "9.11.13",
Release: "3.el8",
Epoch: &epoch,
Arch: "x86_64",
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
},
})
// alpm: the base package differs from the package name, but alpm is not supported so no source reference is expected
catalog.Add(pkg.Package{
Name: "gcc-libs",
Version: "13.2.1-3",
Type: pkg.AlpmPkg,
FoundBy: "alpm-db-cataloger",
Locations: file.NewLocationSet(file.NewLocation("/var/lib/pacman/local/gcc-libs-13.2.1-3/desc")),
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
Metadata: pkg.AlpmDBEntry{
Package: "gcc-libs",
Version: "13.2.1-3",
BasePackage: "gcc",
Architecture: "x86_64",
},
})
return catalog
}
@@ -1,7 +1,6 @@
package binary
import (
"bytes"
"context"
"errors"
"flag"
@@ -21,7 +20,6 @@ import (
"github.com/anchore/syft/internal/testutils"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/binary/internal/manager/testutil"
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
@@ -2178,28 +2176,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.19.0-rc.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-discovery",
Version: "1.19.0-rc.0",
Type: "binary",
PURL: "pkg:generic/istio@1.19.0-rc.0",
Locations: locations("pilot-discovery"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.15.0-beta.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-discovery",
Version: "1.15.0-beta.0",
Type: "binary",
PURL: "pkg:generic/istio@1.15.0-beta.0",
Locations: locations("pilot-discovery"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.10-dev/linux-amd64",
expected: pkg.Package{
@@ -2222,17 +2198,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.5.0-alpha.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-discovery",
Version: "1.5.0-alpha.0",
Type: "binary",
PURL: "pkg:generic/istio@1.5.0-alpha.0",
Locations: locations("pilot-discovery"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.3.8/linux-amd64",
expected: pkg.Package{
@@ -2255,17 +2220,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-discovery/1.0.0-snapshot.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-discovery",
Version: "1.0.0-snapshot.0",
Type: "binary",
PURL: "pkg:generic/istio@1.0.0-snapshot.0",
Locations: locations("pilot-discovery"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.29.0-alpha.0/linux-amd64",
expected: pkg.Package{
@@ -2310,28 +2264,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.19.0-rc.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-agent",
Version: "1.19.0-rc.0",
Type: "binary",
PURL: "pkg:generic/istio@1.19.0-rc.0",
Locations: locations("pilot-agent"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.15.0-beta.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-agent",
Version: "1.15.0-beta.0",
Type: "binary",
PURL: "pkg:generic/istio@1.15.0-beta.0",
Locations: locations("pilot-agent"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.10-dev/linux-amd64",
expected: pkg.Package{
@@ -2354,17 +2286,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.5.0-alpha.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-agent",
Version: "1.5.0-alpha.0",
Type: "binary",
PURL: "pkg:generic/istio@1.5.0-alpha.0",
Locations: locations("pilot-agent"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.1.17/linux-amd64",
expected: pkg.Package{
@@ -2376,17 +2297,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "istio_pilot-agent/1.0.0-snapshot.0/linux-amd64",
expected: pkg.Package{
Name: "pilot-agent",
Version: "1.0.0-snapshot.0",
Type: "binary",
PURL: "pkg:generic/istio@1.0.0-snapshot.0",
Locations: locations("pilot-agent"),
Metadata: metadata("istio-binary"),
},
},
{
logicalFixture: "grafana/12.4.0-22081664032/linux-amd64",
expected: pkg.Package{
@@ -2629,39 +2539,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("grafana-binary"),
},
},
{
logicalFixture: "envoy/1.40.1/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.40.1",
Type: "binary",
PURL: "pkg:generic/envoy@1.40.1",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.40.0/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.40.0",
Type: "binary",
PURL: "pkg:generic/envoy@1.40.0",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.37.1/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.37.1",
Type: "binary",
PURL: "pkg:generic/envoy@1.37.1",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.36.4/linux-amd64",
expected: pkg.Package{
@@ -3315,134 +3192,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
// Test_Cataloger_PackageTypes covers what assertPackagesAreEqual deliberately ignores: the package type and CPEs.
// The perl interpreter is reported as cpan so it lines up with the rest of the CPAN ecosystem, everything else
// keeps the default binary type.
// Test_Cataloger_ffmpeg_versionSuffix verifies the ffmpeg classifiers no longer
// unconditionally truncate the embedded "FFmpeg version <ver>" string to X.Y[.Z].
// Semver build metadata (a backported-patch stamp such as
// "9.0.3+dev.9.0.2.p1.eb9cef3") must be preserved in the reported version, because
// it compares equal to its core version and so does not disturb vulnerability
// matching. A "-" pre-release suffix (a distro revision such as "6.1.1-3ubuntu5")
// must stay truncated: it sorts below the release in semver and would corrupt
// version-range matching. Suffix-free binaries match exactly as before, and
// trailing non-version text (or NUL padding) is never absorbed into the version.
func Test_Cataloger_ffmpeg_versionSuffix(t *testing.T) {
// collect the ffmpeg classifiers under test, keyed by their FileGlob
classifiers := map[string]binutils.Classifier{}
for _, c := range DefaultClassifiers() {
if c.Package == "ffmpeg" {
classifiers[c.FileGlob] = c
}
}
require.NotEmpty(t, classifiers, "expected to find ffmpeg classifiers")
tests := []struct {
name string
fileGlob string // which classifier to run
data string // simulated binary contents
want string // expected reported version ("" = no match expected)
}{
{
name: "library bare version unchanged",
fileGlob: "**/libav*",
data: "FFmpeg version 7.1.1",
want: "7.1.1",
},
{
name: "library two-component version unchanged",
fileGlob: "**/libav*",
data: "FFmpeg version 6.0",
want: "6.0",
},
{
name: "library build metadata with patch provenance preserved",
fileGlob: "**/libav*",
data: "FFmpeg version 9.0.3+dev.9.0.2.p1.eb9cef3",
want: "9.0.3+dev.9.0.2.p1.eb9cef3",
},
{
name: "library build metadata short sha preserved",
fileGlob: "**/libav*",
data: "FFmpeg version 9.0.2+eb9cef3",
want: "9.0.2+eb9cef3",
},
{
// a "-" pre-release suffix is intentionally NOT captured: it sorts below
// the release in semver and would corrupt version-range vulnerability
// matching, so distro revisions stay truncated exactly as before
name: "library distro hyphen revision truncated",
fileGlob: "**/libav*",
data: "FFmpeg version 6.1.1-3ubuntu5",
want: "6.1.1",
},
{
name: "library distro dotted hyphen revision truncated",
fileGlob: "**/libav*",
data: "FFmpeg version 4.4.2-0ubuntu0.22.04.1",
want: "4.4.2",
},
{
name: "library trailing text not captured",
fileGlob: "**/libav*",
data: "FFmpeg version 6.1.1 Copyright (c) 2000-2023 the FFmpeg developers",
want: "6.1.1",
},
{
name: "library NUL-padded version not over-captured",
fileGlob: "**/libav*",
data: "FFmpeg version 9.0.3\x00libavformat.so",
want: "9.0.3",
},
{
name: "libswresample build metadata preserved",
fileGlob: "**/libswresample*",
data: "FFmpeg version 9.0.3+dev.9.0.2.p1.eb9cef3",
want: "9.0.3+dev.9.0.2.p1.eb9cef3",
},
{
// the compiled-in printf format string in the ffmpeg binary is the
// literal "%s version <ver>" (the program name is substituted at runtime)
name: "binary build metadata preserved",
fileGlob: "**/ffmpeg",
data: "%s version 9.0.3+dev.9.0.2.p1.eb9cef3",
want: "9.0.3+dev.9.0.2.p1.eb9cef3",
},
{
name: "binary bare version unchanged",
fileGlob: "**/ffmpeg",
data: "%s version 7.1.1",
want: "7.1.1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cls, ok := classifiers[tt.fileGlob]
require.Truef(t, ok, "no ffmpeg classifier for glob %q", tt.fileGlob)
ctx := binutils.MatcherContext{
GetReader: func(binutils.MatcherContext) (unionreader.UnionReader, error) {
return unionreader.GetUnionReader(io.NopCloser(bytes.NewBufferString(tt.data)))
},
}
pkgs, err := cls.EvidenceMatcher(cls, ctx)
require.NoError(t, err)
if tt.want == "" {
require.Empty(t, pkgs, "expected no package match")
return
}
require.Len(t, pkgs, 1, "expected exactly one package")
assert.Equal(t, tt.want, pkgs[0].Version)
// the fuller version must also flow into the PURL so the SBOM is not lossy;
// compare the decoded PURL version, since packageurl-go percent-encodes
// reserved characters (e.g. '+' -> %2B) in the serialized form
parsed, err := packageurl.FromString(pkgs[0].PURL)
require.NoError(t, err, "PURL should parse")
assert.Equal(t, tt.want, parsed.Version)
})
}
}
func Test_Cataloger_PackageTypes(t *testing.T) {
tests := []struct {
logicalFixture string
+12 -32
View File
@@ -972,10 +972,7 @@ func DefaultClassifiers() []binutils.Classifier {
EvidenceMatcher: m.FileContentsVersionMatcher(
// Pattern found in the binary: "%s version 7.1.1" or "%s version 6.0"
// When executed outputs: "ffmpeg version 7.1.1" or "ffmpeg version 6.0"
// The optional "+" suffix preserves semver build metadata (e.g. a
// backported-patch stamp like "9.0.3+dev.9.0.2.p1.eb9cef3"); see the
// ffmpeg-library classifier for why "-" pre-release suffixes stay truncated.
`(?m)%s version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?(?:\+[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?)`,
`(?m)%s version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`,
),
Package: "ffmpeg",
PURL: mustPURL("pkg:generic/ffmpeg@version"),
@@ -985,15 +982,8 @@ func DefaultClassifiers() []binutils.Classifier {
Class: "ffmpeg-library",
FileGlob: "**/libav*",
EvidenceMatcher: binutils.MatchAny(
// Primary pattern: FFmpeg version found in most libraries.
// The optional "+" suffix preserves semver build metadata (e.g. a
// backported-patch stamp like "9.0.3+dev.9.0.2.p1.eb9cef3") instead of
// truncating to X.Y[.Z]. Build metadata compares equal to its core
// version, so this does not change vulnerability matching. A "-"
// pre-release suffix (e.g. a distro revision "6.1.1-3ubuntu5") is
// deliberately NOT captured: it sorts below the release in semver and
// would corrupt version-range matching, so it stays truncated as before.
m.FileContentsVersionMatcher(`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?(?:\+[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?)`),
// Primary pattern: FFmpeg version found in most libraries
m.FileContentsVersionMatcher(`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`),
// Fallback: library-specific version patterns for libavcodec and libavformat
m.FileContentsVersionMatcher(`(?m)Lavc(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
m.FileContentsVersionMatcher(`(?m)Lavf(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
@@ -1006,9 +996,8 @@ func DefaultClassifiers() []binutils.Classifier {
Class: "ffmpeg-library",
FileGlob: "**/libswresample*",
EvidenceMatcher: m.FileContentsVersionMatcher(
// FFmpeg version pattern for libswresample. Same optional "+" build
// metadata handling as the ffmpeg-library classifier above.
`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?(?:\+[0-9A-Za-z]+(?:\.[0-9A-Za-z]+)*)?)`),
// FFmpeg version pattern for libswresample
`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`),
Package: "ffmpeg",
PURL: mustPURL("pkg:generic/ffmpeg@version"),
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -1042,16 +1031,13 @@ func DefaultClassifiers() []binutils.Classifier {
EvidenceMatcher: binutils.MatchAny(
// [NUL]1.26.8[NUL][NUL]1.26.8[NUL]
// [NUL]1.3.7[NUL][NUL][NUL]1.3.8[NUL]
// [NUL]1.15.0-beta.0[NUL][NUL][NUL]1.15.0-beta.0[NUL] (pre-release builds repeat the full version, suffix included)
m.FileContentsVersionMatcher(`[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+`),
// [NUL][NUL][NUL]1.0.0-snapshot.0[NUL][NUL][NUL] (snapshot builds record the version only once)
m.FileContentsVersionMatcher(`\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+-snapshot\.[0-9]+)\x00+`),
m.FileContentsVersionMatcher(`[0-9]+\.[0-9]+\.[0-9]+\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+`),
// Clean[NUL][NUL][NUL]1.8.0[NUL]
m.FileContentsVersionMatcher(`Clean\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+`),
m.FileContentsVersionMatcher(`Clean\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+`),
// Modified[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]1.10-dev[NUL][NUL][NUL]
m.FileContentsVersionMatcher(`Modified\x00+(?P<version>[0-9]+\.[0-9]+-dev)\x00+`),
// 1.1.17[NUL]...S=v<y5
m.FileContentsVersionMatcher(`(?s)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+.{1,100}S?=v<y5`),
m.FileContentsVersionMatcher(`(?s)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+.{1,100}S?=v<y5`),
),
Package: "pilot-discovery",
PURL: mustPURL("pkg:generic/istio@version"),
@@ -1062,16 +1048,13 @@ func DefaultClassifiers() []binutils.Classifier {
FileGlob: "**/pilot-agent",
EvidenceMatcher: binutils.MatchAny(
// [NUL]1.26.8[NUL][NUL]1.26.8[NUL]
// [NUL]1.15.0-beta.0[NUL][NUL][NUL]1.15.0-beta.0[NUL] (pre-release builds repeat the full version, suffix included)
m.FileContentsVersionMatcher(`[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+`),
// [NUL][NUL][NUL]1.0.0-snapshot.0[NUL][NUL][NUL] (snapshot builds record the version only once)
m.FileContentsVersionMatcher(`\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+-snapshot\.[0-9]+)\x00+`),
m.FileContentsVersionMatcher(`[0-9]+\.[0-9]+\.[0-9]+\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+`),
// Clean[NUL][NUL][NUL]1.8.0[NUL]
m.FileContentsVersionMatcher(`Clean\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+`),
m.FileContentsVersionMatcher(`Clean\x00+(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+`),
// Modified[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]1.10-dev[NUL][NUL][NUL]
m.FileContentsVersionMatcher(`Modified\x00+(?P<version>[0-9]+\.[0-9]+-dev)\x00+`),
// 1.1.17[NUL]...S=v<y5
m.FileContentsVersionMatcher(`(?s)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-snapshot\.[0-9]+|-dev)?)\x00+.{1,100}S?=v<y5`),
m.FileContentsVersionMatcher(`(?s)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]+|-beta\.[0-9]+|-rc\.[0-9]+|-dev)?)\x00+.{1,100}S?=v<y5`),
),
Package: "pilot-agent",
PURL: mustPURL("pkg:generic/istio@version"),
@@ -1135,12 +1118,9 @@ func DefaultClassifiers() []binutils.Classifier {
FileGlob: "**/envoy",
EvidenceMatcher: binutils.MatchAny(
// 1.3x [NUL]1.36.4[NUL]...envoy_reloadable_features
// 1.4x [NUL]1.40.1[NUL]...envoy_reloadable_features
// 1.34.5 [NUL]1.34.5[NUL]...envoy.reloadable_features
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.[34][0-9]\.[0-9]+(-dev)?)\x00.{0,1000}envoy_reloadable_features`),
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00.{0,1000}envoy_reloadable_features`),
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.34\.5)\x00.{0,200}envoy\.reloadable_features`),
// 1.37.1 DeltaDiscoveryResponse.removed_resources[NUL]1.37.1[NUL]the default now-
m.FileContentsVersionMatcher(`(?s)DeltaDiscoveryResponse\.removed_resources\x00(?P<version>1\.[34][0-9]\.[0-9]+(-dev)?)\x00`),
// 1.2x envoy_quic_...[NUL]1.28.7[NUL]
m.FileContentsVersionMatcher(`(?s)envoy_quic_.{0,1000}\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00`),
// 1.2x [NUL]1.20.7[NUL]Unable to
-85
View File
@@ -1203,22 +1203,6 @@ from-images:
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.19.0-rc.0
images:
- ref: istio/pilot:1.19.0-rc.0@sha256:09c3c80be369a9724f11e1edfc60cc0b4d58612a6a99ac7314f620254986b159
platform: linux/amd64
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.15.0-beta.0
images:
- ref: istio/pilot:1.15.0-beta.0@sha256:07a1fd1b14ce51c47f0051f597e9b581020a2e5cc53d144592e85f3fa07bbef6
platform: linux/amd64
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.10-dev
images:
@@ -1235,14 +1219,6 @@ from-images:
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.5.0-alpha.0
images:
- ref: istio/pilot:1.5.0-alpha.0@sha256:b372bcbacd5af241afe097bd017e16773514b6ba17152ca02b7d5285e290b110
platform: linux/amd64
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.3.8
images:
@@ -1259,14 +1235,6 @@ from-images:
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-discovery
version: 1.0.0-snapshot.0
images:
- ref: istio/pilot:1.0.0-snapshot.0@sha256:3b32619b36014eeafaad8199d8af48f7725e9e75a01c0b1abfba1d7166e53b66
platform: linux/amd64
paths:
- /usr/local/bin/pilot-discovery
- name: istio_pilot-agent
version: 1.29.0-alpha.0
images:
@@ -1299,22 +1267,6 @@ from-images:
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.19.0-rc.0
images:
- ref: istio/proxyv2:1.19.0-rc.0@sha256:8c23abb1663e83ec19cac1e3de46c3cf7e942329936463646ca7a8d8088806b4
platform: linux/amd64
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.15.0-beta.0
images:
- ref: istio/proxyv2:1.15.0-beta.0@sha256:9f54e6fa64c6ef6bb8b3e72e513c4f0415f1576ae755b4fe9a5be8c8b38694de
platform: linux/amd64
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.10-dev
images:
@@ -1331,14 +1283,6 @@ from-images:
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.5.0-alpha.0
images:
- ref: istio/proxyv2:1.5.0-alpha.0@sha256:6d31b7e373622b2d483124bdc46974ff7b39c9b3fff8a9dc73a47d91344a24f6
platform: linux/amd64
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.1.17
images:
@@ -1347,14 +1291,6 @@ from-images:
paths:
- /usr/local/bin/pilot-agent
- name: istio_pilot-agent
version: 1.0.0-snapshot.0
images:
- ref: istio/proxyv2:1.0.0-snapshot.0@sha256:13c352cd8b82c326d6e36a8d0a1eed9debe6b1816e30cfe79970d316c9e59714
platform: linux/amd64
paths:
- /usr/local/bin/pilot-agent
- name: grafana
version: 12.3.1
images:
@@ -1579,27 +1515,6 @@ from-images:
paths:
- /usr/lib/x86_64-linux-gnu/libkrb5.so.26.0.0
- version: 1.40.1
images:
- ref: envoyproxy/envoy:v1.40.1@sha256:713bb146f1a7c97ebc70e13d259f77ff36f2c5d00f3145677816af5af9bd1530
platform: linux/amd64
paths:
- /usr/local/bin/envoy
- version: 1.40.0
images:
- ref: envoyproxy/envoy:v1.40.0@sha256:459f2a6404c340fe6afccd69d85527012d47f3c036765493e6f8f3113fa384de
platform: linux/amd64
paths:
- /usr/local/bin/envoy
- version: 1.37.1
images:
- ref: envoyproxy/envoy:v1.37.1@sha256:29496a88fba9c4c9cdef4afe8fec70f536c5ba111b1c2bddbc5436b091ceca33
platform: linux/amd64
paths:
- /usr/local/bin/envoy
- version: 1.36.4
images:
- ref: envoyproxy/envoy:v1.36.4@sha256:ae31562b8cede20913a2d3d6a4f44c8479a50551e033cb8ef7bb8e38cec4b573
-12
View File
@@ -10,7 +10,6 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format"
"github.com/anchore/syft/syft/pkg"
@@ -59,7 +58,6 @@ func parseSBOM(_ context.Context, resolver file.Resolver, _ *generic.Environment
p.FoundBy = catalogerName
p.Type = pkg.BitnamiPkg
setApplicationPart(p.CPEs)
// replace all locations on the package with the location of the SBOM file.
// Why not keep the original list of locations? Since the "locations" field is meant to capture
// where there is evidence of this file, and the catalogers have not run against any file other than,
@@ -184,13 +182,3 @@ func findMainPkgID(relationships []artifact.Relationship) artifact.ID {
return ""
}
// setApplicationPart marks CPEs with an unspecified part as applications, since Bitnami SBOMs
// describe application packages but leave the CPE part as a wildcard.
func setApplicationPart(cpes []cpe.CPE) {
for i := range cpes {
if cpes[i].Attributes.Part == cpe.Any {
cpes[i].Attributes.Part = "a"
}
}
}
+17 -17
View File
@@ -39,7 +39,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/postgresql@17.2.0-8?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:postgresql:postgresql:17.2.0:*:*:*:*:*:*:*",
"cpe:2.3:*:postgresql:postgresql:17.2.0:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "postgresql",
@@ -66,7 +66,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/geos@3.13.0?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:libgeos:geos:3.13.0:*:*:*:*:*:*:*",
"cpe:2.3:*:libgeos:geos:3.13.0:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "geos",
@@ -88,7 +88,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/proj@6.3.2?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:proj:proj:6.3.2:*:*:*:*:*:*:*",
"cpe:2.3:*:proj:proj:6.3.2:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "proj",
@@ -110,7 +110,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/gdal@3.10.1?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:osgeo:gdal:3.10.1:*:*:*:*:*:*:*",
"cpe:2.3:*:osgeo:gdal:3.10.1:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "gdal",
@@ -132,7 +132,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/json-c@0.16.20220414?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:json-c_project:json-c:0.16.20220414:*:*:*:*:*:*:*",
"cpe:2.3:*:json-c_project:json-c:0.16.20220414:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "json-c",
@@ -154,7 +154,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/orafce@4.14.1?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:orafce:orafce:4.14.1:*:*:*:*:*:*:*",
"cpe:2.3:*:orafce:orafce:4.14.1:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "orafce",
@@ -176,7 +176,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/pljava@1.6.8?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:pl/java_project:pl/java:1.6.8:*:*:*:*:*:*:*",
"cpe:2.3:*:pl/java_project:pl/java:1.6.8:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "pljava",
@@ -203,7 +203,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/unixodbc@2.3.12?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:unixodbc:unixodbc:2.3.12:*:*:*:*:*:*:*",
"cpe:2.3:*:unixodbc:unixodbc:2.3.12:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "unixodbc",
@@ -225,7 +225,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/psqlodbc@16.0.0?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:postgresql:psqlodbc:16.0.0:*:*:*:*:*:*:*",
"cpe:2.3:*:postgresql:psqlodbc:16.0.0:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "psqlodbc",
@@ -247,7 +247,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/protobuf@3.21.12?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:golang:protobuf:3.21.12:*:*:*:*:*:*:*",
"cpe:2.3:*:golang:protobuf:3.21.12:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "protobuf",
@@ -269,7 +269,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/protobuf-c@1.5.1?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:protobuf-c:protobuf-c:1.5.1:*:*:*:*:*:*:*",
"cpe:2.3:*:protobuf-c:protobuf-c:1.5.1:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "protobuf-c",
@@ -291,7 +291,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/postgis@3.4.4?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:postgis:postgis:3.4.4:*:*:*:*:*:*:*",
"cpe:2.3:*:postgis:postgis:3.4.4:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "postgis",
@@ -332,7 +332,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/pgbackrest@2.54.2?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:pgbackrest:pgbackrest:2.54.2:*:*:*:*:*:*:*",
"cpe:2.3:*:pgbackrest:pgbackrest:2.54.2:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "pgbackrest",
@@ -354,7 +354,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/wal2json@2.6.0?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:wal2json:wal2json:2.6.0:*:*:*:*:*:*:*",
"cpe:2.3:*:wal2json:wal2json:2.6.0:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "wal2json",
@@ -376,7 +376,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/nss_wrapper@1.1.16?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:nss_wrapper:nss_wrapper:1.1.16:*:*:*:*:*:*:*",
"cpe:2.3:*:nss_wrapper:nss_wrapper:1.1.16:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "nss_wrapper",
@@ -412,7 +412,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/render-template@1.0.7-4?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:render-template:render-template:1.0.7:*:*:*:*:*:*:*",
"cpe:2.3:*:render-template:render-template:1.0.7:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "render-template",
@@ -437,7 +437,7 @@ func TestBitnamiCataloger(t *testing.T) {
FoundBy: catalogerName,
PURL: "pkg:bitnami/redis@7.4.0-0?arch=arm64&distro=debian-12",
CPEs: mustCPEs(
"cpe:2.3:a:redis:redis:7.4.0:*:*:*:*:*:*:*",
"cpe:2.3:*:redis:redis:7.4.0:*:*:*:*:*:*:*",
),
Metadata: &pkg.BitnamiSBOMEntry{
Name: "redis",
+3 -3
View File
@@ -138,9 +138,9 @@ func (c *goModCataloger) loadPackages(modDir string, loc file.Location) (pkgs ma
for _, p := range rootPkgs {
if len(p.Errors) > 0 {
// Log errors but continue processing
for _, pe := range p.Errors {
log.Debugf("package load error for %s: %v", p.PkgPath, pe)
unknownErr = unknown.Append(unknownErr, loc, pe)
for _, e := range p.Errors {
log.Debugf("package load error for %s: %v", p.PkgPath, e)
unknownErr = unknown.Append(unknownErr, loc, err)
}
}
}
@@ -2434,8 +2434,7 @@
"cpe:2.3:a:linuxfoundation:backstage\\/integration:*:*:*:*:*:node.js:*:*"
],
"@backstage/plugin-auth-backend": [
"cpe:2.3:a:linuxfoundation:auth_backend:*:*:*:*:*:node.js:*:*",
"cpe:2.3:a:linuxfoundation:backstage\\/plugin-auth-backend:*:*:*:*:*:node.js:*:*"
"cpe:2.3:a:linuxfoundation:auth_backend:*:*:*:*:*:node.js:*:*"
],
"@backstage/plugin-catalog-backend": [
"cpe:2.3:a:linuxfoundation:backstage\\/plugin-catalog-backend-module-unprocessed:*:*:*:*:*:node.js:*:*"
@@ -6579,15 +6578,9 @@
"Pillow": [
"cpe:2.3:a:python:pillow:*:*:*:*:*:*:*:*"
],
"PraisonAI": [
"cpe:2.3:a:praison:praisonai:*:*:*:*:*:*:*:*"
],
"PyAMF": [
"cpe:2.3:a:pyamf:pyamf:*:*:*:*:*:*:*:*"
],
"PyJWT": [
"cpe:2.3:a:pyjwt_project:pyjwt:*:*:*:*:*:*:*:*"
],
"PyPDF2": [
"cpe:2.3:a:pypdf2_project:pypdf2:*:*:*:*:*:*:*:*"
],
@@ -7887,9 +7880,6 @@
"anchor-lang": [
"cpe:2.3:a:anchor-lang:anchor:*:*:*:*:*:rust:*:*"
],
"anode": [
"cpe:2.3:a:obsidiandynamics:anode:*:*:*:*:*:rust:*:*"
],
"anymap": [
"cpe:2.3:a:anymap_project:anymap:*:*:*:*:*:rust:*:*"
],
@@ -8013,9 +8003,6 @@
"capnp": [
"cpe:2.3:a:capnproto:capnp:*:*:*:*:*:rust:*:*"
],
"cargo": [
"cpe:2.3:a:rust-lang:cargo:*:*:*:*:*:rust:*:*"
],
"cbox": [
"cpe:2.3:a:cbox_project:cbox:*:*:*:*:*:rust:*:*"
],
@@ -10583,9 +10570,6 @@
"captcha": [
"cpe:2.3:a:bestwebsoft:captcha:*:*:*:*:*:wordpress:*:*"
],
"captcha-bank": [
"cpe:2.3:a:tech-banker:captcha_bank:*:*:*:*:*:wordpress:*:*"
],
"captchinoo-captcha-for-login-form-protection": [
"cpe:2.3:a:wp-buy:captchinoo:*:*:*:*:*:wordpress:*:*"
],
@@ -18670,9 +18654,6 @@
"sharable-password-protected-posts": [
"cpe:2.3:a:fabiantodt:private_post_share:*:*:*:*:*:wordpress:*:*"
],
"share-button": [
"cpe:2.3:a:maxfoundry:social_share_buttons:*:*:*:*:*:wordpress:*:*"
],
"share-buttons": [
"cpe:2.3:a:artlosk:share_buttons:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:artlosk:social_share_buttons:*:*:*:*:*:wordpress:*:*"
@@ -141,16 +141,9 @@ func FromDictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
// generate the minimal set of representative CPEs, which implies that optional fields should not be included
// (such as target SW).
func FromPackageAttributes(p pkg.Package) []cpe.CPE {
switch p.Type {
case pkg.KbPkg:
// Microsoft KB packages are patches, not products: the name is an MSRC product ID and the
// version a KB number, neither of which maps onto a CPE vendor, product or version. They are
// matched against MSRC data by name and version, so a generated CPE is only noise, and one
// that NVD-based matching could misread.
return nil
case pkg.ModelPkg:
// ML models should not have CPEs as they are not traditional software packages
// and don't fit the vulnerability model used for software packages.
// ML models should not have CPEs as they are not traditional software packages
// and don't fit the vulnerability model used for software packages.
if p.Type == pkg.ModelPkg {
return nil
}
@@ -892,16 +892,6 @@ func TestGeneratePackageCPEs(t *testing.T) {
},
expected: []string{},
},
{
name: "Microsoft KB package should generate no CPEs",
p: pkg.Package{
Name: "12437",
Version: "935509",
Type: pkg.KbPkg,
Metadata: pkg.MicrosoftKbPatch{ProductID: "12437", Kb: "935509"},
},
expected: []string{},
},
{
name: "rust package",
p: pkg.Package{
@@ -35,11 +35,6 @@ func parseGradleLockfile(_ context.Context, _ file.Resolver, _ *generic.Environm
// Trim leading and trailing whitespace from the line
line = strings.TrimSpace(line)
// skip comment lines (e.g. the header gradle writes), which may contain ':' and '=' characters
if strings.HasPrefix(line, "#") {
continue
}
groupNameVersion := line
groupNameVersion = strings.Split(groupNameVersion, "=")[0]
parts := strings.Split(groupNameVersion, ":")
@@ -87,19 +87,3 @@ func Test_parserGradleLockfile_lineTooLong(t *testing.T) {
require.Len(t, pkgs, 1)
assert.Equal(t, "before", pkgs[0].Name)
}
func Test_parserGradleLockfile_skipsComments(t *testing.T) {
contents := `# This is a Gradle generated file for dependency locking.
# To regenerate this file, run: ./gradlew :dependencies --write-locks
# indented: comment=too
com.squareup.okhttp3:okhttp:4.12.0=compileClasspath,runtimeClasspath
empty=annotationProcessor
`
reader := file.NewLocationReadCloser(file.NewLocation("gradle.lockfile"), io.NopCloser(strings.NewReader(contents)))
pkgs, _, err := parseGradleLockfile(context.Background(), nil, nil, reader)
require.NoError(t, err)
require.Len(t, pkgs, 1)
assert.Equal(t, "okhttp", pkgs[0].Name)
assert.Equal(t, "4.12.0", pkgs[0].Version)
}