Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6CO
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
Expand Down
2 changes: 2 additions & 0 deletions pkg/attestation/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func (c *ExtractorChain) ExtractAll(attestations []TypedAttestation, typeFilter
extracted := extractor.Extract(att.Data)

for _, f := range extracted {
ValidatePath(f.Path)

if _, seen := seenPaths[f.Path]; !seen {
seenPaths[f.Path] = struct{}{}
files = append(files, f)
Expand Down
32 changes: 32 additions & 0 deletions pkg/attestation/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package attestation

import (
"fmt"
"os"

"github.com/bmatcuk/doublestar/v4"
)

// ValidatePath checks if an attestation path looks suspicious (e.g. logs or git directories).
// It prints a warning but does not enforce exclusion.
func ValidatePath(path string) {
if matchesPattern(path) {
fmt.Fprintf(os.Stderr, "WARNING: unexpected file in attestation: %s\n", path)
}
}

func matchesPattern(path string) bool {
patterns := []string{
"**/*.log",
"**/.git/**",
".git/**",
}

for _, pattern := range patterns {
if matched, err := doublestar.Match(pattern, path); err == nil && matched {
return true
}
}

return false
}
81 changes: 81 additions & 0 deletions pkg/attestation/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package attestation

import (
"bytes"
"io"
"os"
"strings"
"testing"
)

func TestMatchesPattern(t *testing.T) {
tests := []struct {
name string
path string
expected bool
}{
// Positive cases
{"Git config", ".git/config", true},
{"Nested git hooks", "repo/.git/hooks/pre-commit", true},
{"Git directory itself", ".git", true},
{"App log", "logs/app.log", true},
{"Absolute path log", "/var/tmp/debug/output.log", true},

// Negative cases
{"Go source", "src/main.go", false},
{"Markdown reader", "README.md", false},
{"HTML docs", "docs/index.html", false},
{"Empty path", "", false},
{"Subtly spoofed git", "mygit/config", false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := matchesPattern(tt.path)
if result != tt.expected {
t.Errorf("matchesPattern(%q) = %v; want %v", tt.path, result, tt.expected)
}
})
}
}

func TestValidatePath(t *testing.T) {
// captureStderr is a helper to intercept os.Stderr output
captureStderr := func(f func()) string {
origStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

// Execute function
f()

w.Close()
os.Stderr = origStderr

var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
return buf.String()
}

t.Run("Suspicious path prints warning", func(t *testing.T) {
path := ".git/config"
output := captureStderr(func() {
ValidatePath(path)
})

if !strings.Contains(output, "WARNING") {
t.Errorf("ValidatePath(%q) expected to print warning, got output: %q", path, output)
}
})

t.Run("Safe path does not print warning", func(t *testing.T) {
path := "src/main.go"
output := captureStderr(func() {
ValidatePath(path)
})

if output != "" {
t.Errorf("ValidatePath(%q) expected no output, got: %q", path, output)
}
})
}
2 changes: 1 addition & 1 deletion pkg/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type PackageInfo struct {
PURL string `json:"purl"`
Licenses []string `json:"licenses,omitempty"`
Hashes map[string]string `json:"hashes,omitempty"`
FoundBy string `json:"found_by"` // which resolver found this
FoundBy string `json:"found_by"` // which resolver found this
DownloadURL string `json:"download_url,omitempty"` // set by network resolvers
DownloadIP string `json:"download_ip,omitempty"` // set by network resolvers
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/resolver/rust.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package resolver
import (
"path"
"regexp"
"sort"
"strings"
)

Expand Down Expand Up @@ -73,7 +74,14 @@ func (r *RustResolver) Resolve(files []FileInfo) (packages []PackageInfo, remain
remainingFiles = append(remainingFiles, f)
}

for name, version := range chosenByName {
var names []string
for name := range chosenByName {
names = append(names, name)
}
sort.Strings(names)

for _, name := range names {
version := chosenByName[name]
if name == "" || version == "" {
continue
}
Expand Down
44 changes: 44 additions & 0 deletions pkg/resolver/rust_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package resolver

import (
"testing"
)

func TestRustResolver_DeterministicOrder(t *testing.T) {
r := NewRustResolver()

files := []FileInfo{
{Path: "/registry/src/github.com/a/tokio-1.0.0/src/lib.rs"},
{Path: "/registry/src/github.com/a/serde-1.0.0/src/lib.rs"},
}

pkgs, _ := r.Resolve(files)

if len(pkgs) != 2 {
t.Fatalf("expected 2 packages, got %d", len(pkgs))
}

if pkgs[0].Name != "serde" {
t.Errorf("expected serde first, got %s", pkgs[0].Name)
}

if pkgs[1].Name != "tokio" {
t.Errorf("expected tokio second, got %s", pkgs[1].Name)
}
}

func TestRustResolver_StableOutput(t *testing.T) {
r := NewRustResolver()

files := []FileInfo{
{Path: "/registry/src/github.com/a/zeta-1.0.0/src/lib.rs"},
{Path: "/registry/src/github.com/a/alpha-1.0.0/src/lib.rs"},
}

pkgs1, _ := r.Resolve(files)
pkgs2, _ := r.Resolve(files)

if pkgs1[0].Name != pkgs2[0].Name {
t.Errorf("output is not stable across runs")
}
}