From 29fe5cb8fb78c1e29c756137dc61de5b2651d17d Mon Sep 17 00:00:00 2001 From: imshubham22apr-gif Date: Wed, 15 Apr 2026 21:06:49 +0530 Subject: [PATCH 1/2] docs: sync README.md with cmd/generate.go defaults and fix pipeline flow (fixes #28) Signed-off-by: imshubham22apr-gif --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7507fe6..49ec0b1 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Flags: -v, --version string Version for the SBOM document (default "0.0.1") ``` -By default, `sbomit` parses `material`, `command-run`, and `product` attestations. To restrict parsing on demand: +By default, `sbomit` parses `material`, `command-run`, `product`, and `network-trace` attestations. To restrict parsing on demand: ```bash sbomit generate attestation.json --types command-run @@ -55,9 +55,10 @@ sbomit generate attestation.json --catalog syft --project-dir /path/to/project ### Attestation Extractors Modular extractors for different attestation types: -- `MaterialExtractor` - Build Input materials +- `MaterialExtractor` - Build input materials - `CommandRunExtractor` - Opened files from processes - `ProductExtractor` - Built artifacts +- `NetworkTrace` - External download connections Implement `Extractor` interface to add new types. @@ -74,8 +75,9 @@ Each resolver implements `Resolver` and optionally `PackageFileFilterer` to filt ### Processing Pipeline ``` -Attestation → Extract Files → Filter Cache Files → -Run Resolvers → Filter Package Files → Generate SBOM +Attestation → Extract Files & Network Conns → Run Resolvers → +Filter Package Files → Resolve Network PURLs → Merge with Catalog (Syft) → +Generate SBOM Document ``` ## Testing From e57a3aa0795d38adad4490d6901f9fa79550d39c Mon Sep 17 00:00:00 2001 From: imshubham22apr-gif Date: Wed, 15 Apr 2026 21:19:07 +0530 Subject: [PATCH 2/2] test: add unit tests for rust resolver (fixes #6) Signed-off-by: imshubham22apr-gif --- pkg/resolver/rust_test.go | 168 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 pkg/resolver/rust_test.go diff --git a/pkg/resolver/rust_test.go b/pkg/resolver/rust_test.go new file mode 100644 index 0000000..03580e0 --- /dev/null +++ b/pkg/resolver/rust_test.go @@ -0,0 +1,168 @@ +package resolver + +import ( + "testing" +) + +func TestRustResolver_Resolve(t *testing.T) { + tests := []struct { + name string + files []FileInfo + wantPackages []PackageInfo + wantRemaining int + }{ + { + name: "Valid Cargo registry path", + files: []FileInfo{ + {Path: "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.130/src/lib.rs"}, + }, + wantPackages: []PackageInfo{ + { + Name: "serde", + Version: "1.0.130", + Ecosystem: "cargo", + PURL: "pkg:cargo/serde@1.0.130", + }, + }, + wantRemaining: 1, + }, + { + name: "Cargo cache crate file", + files: []FileInfo{ + {Path: "/home/user/.cargo/registry/cache/github.com-1ecc6299db9ec823/tokio-1.28.2.crate"}, + }, + wantPackages: []PackageInfo{ + { + Name: "tokio", + Version: "1.28.2", + Ecosystem: "cargo", + PURL: "pkg:cargo/tokio@1.28.2", + }, + }, + wantRemaining: 1, + }, + { + name: "Multiple crates and ignored files", + files: []FileInfo{ + {Path: "/home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.4/src/lib.rs"}, + {Path: "/home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.4/target/debug/libregex.rlib"}, + {Path: "/home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.147/src/lib.rs"}, + }, + wantPackages: []PackageInfo{ + {Name: "regex", Version: "1.8.4", Ecosystem: "cargo", PURL: "pkg:cargo/regex@1.8.4"}, + {Name: "libc", Version: "0.2.147", Ecosystem: "cargo", PURL: "pkg:cargo/libc@0.2.147"}, + }, + wantRemaining: 2, + }, + { + name: "Path with version-like string in directory name but not crate", + files: []FileInfo{ + {Path: "/home/user/not-a-registry/some-package-1.2.3/src/main.rs"}, + }, + wantPackages: nil, + wantRemaining: 1, + }, + { + name: "Malformed version string", + files: []FileInfo{ + {Path: "/usr/local/cargo/registry/src/unrecognized/invalid-pkg/src/lib.rs"}, + }, + wantPackages: nil, + wantRemaining: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRustResolver() + gotPackages, gotRemaining := r.Resolve(tt.files) + + if len(gotPackages) != len(tt.wantPackages) { + t.Errorf("Resolve() gotPackages length = %v, want %v", len(gotPackages), len(tt.wantPackages)) + return + } + + for _, wp := range tt.wantPackages { + found := false + for _, gp := range gotPackages { + if gp.Name == wp.Name && gp.Version == wp.Version && gp.PURL == wp.PURL { + found = true + break + } + } + if !found { + t.Errorf("Resolve() did not find expected package %+v", wp) + } + } + + if len(gotRemaining) != tt.wantRemaining { + t.Errorf("Resolve() gotRemaining length = %v, want %v", len(gotRemaining), tt.wantRemaining) + } + }) + } +} + +func TestRustPackageFilter_Matches(t *testing.T) { + tests := []struct { + name string + packageName string + version string + path string + want bool + }{ + { + name: "Match registry src path", + packageName: "serde", + version: "1.0.130", + path: "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.130/src/lib.rs", + want: true, + }, + { + name: "Match registry cache crate", + packageName: "tokio", + version: "1.28.2", + path: "/home/user/.cargo/registry/cache/github.com-1ecc6299db9ec823/tokio-1.28.2.crate", + want: true, + }, + { + name: "Match from crates directory", + packageName: "anyhow", + version: "1.0.71", + path: "/usr/local/cargo/crates/anyhow-1.0.71/src/lib.rs", + want: true, + }, + { + name: "Mismatch version", + packageName: "serde", + version: "1.0.130", + path: "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.131/src/lib.rs", + want: false, + }, + { + name: "Mismatch package name", + packageName: "serde", + version: "1.0.130", + path: "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.130/src/lib.rs", + want: false, + }, + { + name: "Non-registry path", + packageName: "serde", + version: "1.0.130", + path: "/home/user/not-a-registry/serde-1.0.130/src/lib.rs", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &rustPackageFilter{ + packageName: tt.packageName, + version: tt.version, + } + if got := f.Matches(tt.path); got != tt.want { + t.Errorf("Matches() = %v, want %v", got, tt.want) + } + }) + } +}