From 34ecc8cc6f5d2b24d8d50ee274c7f908fc98ca93 Mon Sep 17 00:00:00 2001 From: Sandipmandal25 Date: Mon, 6 Apr 2026 22:24:01 +0530 Subject: [PATCH] test: add network resolver tests for Go, JavaScript, and Rust Signed-off-by: Sandipmandal25 --- pkg/resolver/network/go_test.go | 166 ++++++++++++++++++++++++ pkg/resolver/network/javascript_test.go | 145 +++++++++++++++++++++ pkg/resolver/network/rust_test.go | 155 ++++++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 pkg/resolver/network/go_test.go create mode 100644 pkg/resolver/network/javascript_test.go create mode 100644 pkg/resolver/network/rust_test.go diff --git a/pkg/resolver/network/go_test.go b/pkg/resolver/network/go_test.go new file mode 100644 index 0000000..76697e1 --- /dev/null +++ b/pkg/resolver/network/go_test.go @@ -0,0 +1,166 @@ +package network + +import ( + "testing" +) + +func TestGoNetworkResolver(t *testing.T) { + tests := []struct { + name string + hostname string + url string + referer string + statusCode int + bodyHash string + expectPkg bool + expectedName string + expectedVer string + expectedHash string + }{ + { + name: "resolves zip from proxy.golang.org", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/cilium/ebpf/@v/v0.20.0.zip", + statusCode: 200, + expectPkg: true, + expectedName: "github.com/cilium/ebpf", + expectedVer: "v0.20.0", + }, + { + name: "resolves mod file", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/pkg/errors/@v/v0.9.1.mod", + statusCode: 200, + expectPkg: true, + expectedName: "github.com/pkg/errors", + expectedVer: "v0.9.1", + }, + { + name: "resolves info file", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/golang.org/x/net/@v/v0.21.0.info", + statusCode: 200, + expectPkg: true, + expectedName: "golang.org/x/net", + expectedVer: "v0.21.0", + }, + { + name: "decodes uppercase-encoded module path", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/!burnt!sushi/toml/@v/v1.3.2.zip", + statusCode: 200, + expectPkg: true, + expectedName: "github.com/BurntSushi/toml", + expectedVer: "v1.3.2", + }, + { + name: "resolves from storage.googleapis.com via proxy Referer", + hostname: "storage.googleapis.com", + url: "https://storage.googleapis.com/gomodules/abc123", + referer: "https://proxy.golang.org/github.com/pkg/errors/@v/v0.9.1.zip", + statusCode: 200, + expectPkg: true, + expectedName: "github.com/pkg/errors", + expectedVer: "v0.9.1", + }, + { + name: "storage.googleapis.com without proxy Referer produces no package", + hostname: "storage.googleapis.com", + url: "https://storage.googleapis.com/gomodules/abc123", + referer: "https://other.cdn.com/path", + statusCode: 200, + expectPkg: false, + }, + { + name: "skips 404 status", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/cilium/ebpf/@v/v0.20.0.zip", + statusCode: 404, + expectPkg: false, + }, + { + name: "zero status treated as success", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/cilium/ebpf/@v/v0.20.0.zip", + statusCode: 0, + expectPkg: true, + expectedName: "github.com/cilium/ebpf", + expectedVer: "v0.20.0", + }, + { + name: "non-matching path produces no package", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/sumdb/lookup/github.com/cilium/ebpf@v0.20.0", + statusCode: 200, + expectPkg: false, + }, + { + name: "records body hash as sha256", + hostname: "proxy.golang.org", + url: "https://proxy.golang.org/github.com/cilium/ebpf/@v/v0.20.0.zip", + statusCode: 200, + bodyHash: "deadbeef", + expectPkg: true, + expectedName: "github.com/cilium/ebpf", + expectedVer: "v0.20.0", + expectedHash: "deadbeef", + }, + } + + resolver := NewGoNetworkResolver() + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + conn := NetworkConnection{ + Hostname: tc.hostname, + Exchanges: []NetworkExchange{ + { + URL: tc.url, + Referer: tc.referer, + StatusCode: tc.statusCode, + BodyHash: tc.bodyHash, + }, + }, + } + + pkgs := resolver.Resolve(conn) + + if !tc.expectPkg { + if len(pkgs) != 0 { + t.Errorf("expected no packages, got %d", len(pkgs)) + } + return + } + + if len(pkgs) != 1 { + t.Fatalf("expected 1 package, got %d", len(pkgs)) + } + pkg := pkgs[0] + if pkg.Name != tc.expectedName { + t.Errorf("name = %q, want %q", pkg.Name, tc.expectedName) + } + if pkg.Version != tc.expectedVer { + t.Errorf("version = %q, want %q", pkg.Version, tc.expectedVer) + } + if tc.expectedHash != "" && pkg.Hashes["sha256"] != tc.expectedHash { + t.Errorf("hash = %q, want %q", pkg.Hashes["sha256"], tc.expectedHash) + } + }) + } +} + +func TestGoNetworkResolver_Deduplication(t *testing.T) { + resolver := NewGoNetworkResolver() + conn := NetworkConnection{ + Hostname: "proxy.golang.org", + Exchanges: []NetworkExchange{ + {URL: "https://proxy.golang.org/github.com/pkg/errors/@v/v0.9.1.zip", StatusCode: 200}, + {URL: "https://proxy.golang.org/github.com/pkg/errors/@v/v0.9.1.mod", StatusCode: 200}, + }, + } + + pkgs := resolver.Resolve(conn) + if len(pkgs) != 1 { + t.Errorf("expected 1 package after dedup of zip+mod, got %d", len(pkgs)) + } +} diff --git a/pkg/resolver/network/javascript_test.go b/pkg/resolver/network/javascript_test.go new file mode 100644 index 0000000..654b6ff --- /dev/null +++ b/pkg/resolver/network/javascript_test.go @@ -0,0 +1,145 @@ +package network + +import ( + "testing" +) + +func TestJavaScriptNetworkResolver(t *testing.T) { + tests := []struct { + name string + url string + statusCode int + bodyHash string + expectPkg bool + expectedName string + expectedVer string + expectedHash string + }{ + { + name: "non-scoped tarball", + url: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + statusCode: 200, + expectPkg: true, + expectedName: "lodash", + expectedVer: "4.17.21", + }, + { + name: "scoped tarball", + url: "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + statusCode: 200, + expectPkg: true, + expectedName: "@babel/core", + expectedVer: "7.24.0", + }, + { + name: "non-scoped metadata endpoint", + url: "https://registry.npmjs.org/express/4.18.2", + statusCode: 200, + expectPkg: true, + expectedName: "express", + expectedVer: "4.18.2", + }, + { + name: "scoped metadata endpoint", + url: "https://registry.npmjs.org/@types/node/18.0.0", + statusCode: 200, + expectPkg: true, + expectedName: "@types/node", + expectedVer: "18.0.0", + }, + { + name: "pre-release version in tarball", + url: "https://registry.npmjs.org/express/-/express-5.0.0-beta.1.tgz", + statusCode: 200, + expectPkg: true, + expectedName: "express", + expectedVer: "5.0.0-beta.1", + }, + { + name: "normalizes package name to lowercase", + url: "https://registry.npmjs.org/@MyOrg/Pkg/-/Pkg-1.0.0.tgz", + statusCode: 200, + expectPkg: true, + expectedName: "@myorg/pkg", + expectedVer: "1.0.0", + }, + { + name: "skips 404 status", + url: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + statusCode: 404, + expectPkg: false, + }, + { + name: "non-matching path produces no package", + url: "https://registry.npmjs.org/-/npm/v1/security/audits", + statusCode: 200, + expectPkg: false, + }, + { + name: "records body hash as sha256", + url: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + statusCode: 200, + bodyHash: "cafebabe", + expectPkg: true, + expectedName: "lodash", + expectedVer: "4.17.21", + expectedHash: "cafebabe", + }, + } + + resolver := NewJavaScriptNetworkResolver() + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + conn := NetworkConnection{ + Hostname: "registry.npmjs.org", + Exchanges: []NetworkExchange{ + { + URL: tc.url, + StatusCode: tc.statusCode, + BodyHash: tc.bodyHash, + }, + }, + } + + pkgs := resolver.Resolve(conn) + + if !tc.expectPkg { + if len(pkgs) != 0 { + t.Errorf("expected no packages, got %d", len(pkgs)) + } + return + } + + if len(pkgs) != 1 { + t.Fatalf("expected 1 package, got %d", len(pkgs)) + } + pkg := pkgs[0] + if pkg.Name != tc.expectedName { + t.Errorf("name = %q, want %q", pkg.Name, tc.expectedName) + } + if pkg.Version != tc.expectedVer { + t.Errorf("version = %q, want %q", pkg.Version, tc.expectedVer) + } + if tc.expectedHash != "" && pkg.Hashes["sha256"] != tc.expectedHash { + t.Errorf("hash = %q, want %q", pkg.Hashes["sha256"], tc.expectedHash) + } + }) + } +} + +func TestJavaScriptNetworkResolver_Deduplication(t *testing.T) { + resolver := NewJavaScriptNetworkResolver() + conn := NetworkConnection{ + Hostname: "registry.npmjs.org", + Exchanges: []NetworkExchange{ + {URL: "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", StatusCode: 200}, + {URL: "https://registry.npmjs.org/lodash/4.17.21", StatusCode: 200}, + }, + } + + pkgs := resolver.Resolve(conn) + if len(pkgs) != 1 { + t.Errorf("expected 1 package after dedup of tgz+metadata, got %d", len(pkgs)) + } +} diff --git a/pkg/resolver/network/rust_test.go b/pkg/resolver/network/rust_test.go new file mode 100644 index 0000000..174a7d2 --- /dev/null +++ b/pkg/resolver/network/rust_test.go @@ -0,0 +1,155 @@ +package network + +import ( + "testing" +) + +func TestRustNetworkResolver(t *testing.T) { + tests := []struct { + name string + hostname string + url string + statusCode int + bodyHash string + expectPkg bool + expectedName string + expectedVer string + expectedHash string + }{ + { + name: "resolves crate file from static.crates.io", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/serde/serde-1.0.200.crate", + statusCode: 200, + expectPkg: true, + expectedName: "serde", + expectedVer: "1.0.200", + }, + { + name: "resolves via API download endpoint", + hostname: "crates.io", + url: "https://crates.io/api/v1/crates/tokio/1.38.0/download", + statusCode: 200, + expectPkg: true, + expectedName: "tokio", + expectedVer: "1.38.0", + }, + { + name: "hyphenated crate name", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/serde-derive/serde-derive-1.0.200.crate", + statusCode: 200, + expectPkg: true, + expectedName: "serde-derive", + expectedVer: "1.0.200", + }, + { + name: "underscore crate name", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/serde_json/serde_json-1.0.117.crate", + statusCode: 200, + expectPkg: true, + expectedName: "serde_json", + expectedVer: "1.0.117", + }, + { + name: "pre-release version in crate file", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/tokio/tokio-1.38.0-beta.1.crate", + statusCode: 200, + expectPkg: true, + expectedName: "tokio", + expectedVer: "1.38.0-beta.1", + }, + { + name: "pre-release version via API endpoint", + hostname: "crates.io", + url: "https://crates.io/api/v1/crates/tokio/1.38.0-beta.1/download", + statusCode: 200, + expectPkg: true, + expectedName: "tokio", + expectedVer: "1.38.0-beta.1", + }, + { + name: "skips 404 status", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/serde/serde-1.0.200.crate", + statusCode: 404, + expectPkg: false, + }, + { + name: "non-matching path produces no package", + hostname: "index.crates.io", + url: "https://index.crates.io/se/rd/serde", + statusCode: 200, + expectPkg: false, + }, + { + name: "records body hash as sha256", + hostname: "static.crates.io", + url: "https://static.crates.io/crates/serde/serde-1.0.200.crate", + statusCode: 200, + bodyHash: "f00dcafe", + expectPkg: true, + expectedName: "serde", + expectedVer: "1.0.200", + expectedHash: "f00dcafe", + }, + } + + resolver := NewRustNetworkResolver() + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + conn := NetworkConnection{ + Hostname: tc.hostname, + Exchanges: []NetworkExchange{ + { + URL: tc.url, + StatusCode: tc.statusCode, + BodyHash: tc.bodyHash, + }, + }, + } + + pkgs := resolver.Resolve(conn) + + if !tc.expectPkg { + if len(pkgs) != 0 { + t.Errorf("expected no packages, got %d", len(pkgs)) + } + return + } + + if len(pkgs) != 1 { + t.Fatalf("expected 1 package, got %d", len(pkgs)) + } + pkg := pkgs[0] + if pkg.Name != tc.expectedName { + t.Errorf("name = %q, want %q", pkg.Name, tc.expectedName) + } + if pkg.Version != tc.expectedVer { + t.Errorf("version = %q, want %q", pkg.Version, tc.expectedVer) + } + if tc.expectedHash != "" && pkg.Hashes["sha256"] != tc.expectedHash { + t.Errorf("hash = %q, want %q", pkg.Hashes["sha256"], tc.expectedHash) + } + }) + } +} + +func TestRustNetworkResolver_Deduplication(t *testing.T) { + resolver := NewRustNetworkResolver() + conn := NetworkConnection{ + Hostname: "static.crates.io", + Exchanges: []NetworkExchange{ + {URL: "https://static.crates.io/crates/tokio/tokio-1.38.0.crate", StatusCode: 200}, + {URL: "https://crates.io/api/v1/crates/tokio/1.38.0/download", StatusCode: 200}, + }, + } + + pkgs := resolver.Resolve(conn) + if len(pkgs) != 1 { + t.Errorf("expected 1 package after dedup of crate file+API, got %d", len(pkgs)) + } +}