From 11164112489ac603a4d752d0b65d77ef8a3eab86 Mon Sep 17 00:00:00 2001 From: Keshav Priyadarshi Date: Wed, 11 Mar 2026 14:42:51 +0530 Subject: [PATCH 1/3] Return error for invalid or unsupported PURLs Signed-off-by: Keshav Priyadarshi --- go.mod | 2 ++ go.sum | 2 ++ purlvalidator.go | 20 ++++++++++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 52327fc..0520025 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.22.3 require github.com/blevesearch/vellum v1.1.0 +require github.com/package-url/packageurl-go v0.1.5 + require ( github.com/bits-and-blooms/bitset v1.2.0 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect diff --git a/go.sum b/go.sum index 277a8cc..eebd71e 100644 --- a/go.sum +++ b/go.sum @@ -4,5 +4,7 @@ github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCD github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= +github.com/package-url/packageurl-go v0.1.5 h1:O4efRXja2XQ5CtiiYiCZ22k/m7i5ugLiAghgcC+eDgk= +github.com/package-url/packageurl-go v0.1.5/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/purlvalidator.go b/purlvalidator.go index 8bc7cf3..6c609c8 100644 --- a/purlvalidator.go +++ b/purlvalidator.go @@ -13,10 +13,11 @@ package purlvalidator import ( _ "embed" + "fmt" "log" - "strings" "github.com/blevesearch/vellum" + "github.com/package-url/packageurl-go" ) //go:embed purls.fst @@ -32,12 +33,19 @@ func init() { } } -func validate_purl(packageURL string, fstMap *vellum.FST) bool { - packageURL = strings.TrimSuffix(packageURL, "/") - result, _ := fstMap.Contains([]byte(packageURL)) - return result +func validate_purl(packageURL string, fstMap *vellum.FST) (bool, error) { + instance, err := packageurl.FromString(packageURL) + if err != nil { + return false, err + } + if instance.Version != "" || len(instance.Qualifiers) > 0 || instance.Subpath != "" { + return false, fmt.Errorf("only base PURL is supported (no version, qualifiers, or subpath)") + } + + result, err := fstMap.Contains([]byte(packageURL)) + return result, err } -func Validate(packageURL string) bool { +func Validate(packageURL string) (bool, error) { return validate_purl(packageURL, validator) } From e14154e3729639cb67fd612c71c795058727c6f4 Mon Sep 17 00:00:00 2001 From: Keshav Priyadarshi Date: Wed, 11 Mar 2026 14:43:23 +0530 Subject: [PATCH 2/3] Add test for unsupported and invalid PURLs Signed-off-by: Keshav Priyadarshi --- purlvalidator_test.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/purlvalidator_test.go b/purlvalidator_test.go index fceb8c6..5ef3772 100644 --- a/purlvalidator_test.go +++ b/purlvalidator_test.go @@ -47,7 +47,7 @@ func setup() { func TestNonexistentPurl(t *testing.T) { purl := "pkg:nuget/nonexistent" - result := validate_purl(purl, testValidator) + result, _ := validate_purl(purl, testValidator) expected := false if result != expected { @@ -57,7 +57,7 @@ func TestNonexistentPurl(t *testing.T) { func TestValidPurl(t *testing.T) { purl := "pkg:nuget/FluentUtils.FromCompositeAttribute" - result := validate_purl(purl, testValidator) + result, _ := validate_purl(purl, testValidator) expected := true if result != expected { @@ -65,12 +65,20 @@ func TestValidPurl(t *testing.T) { } } -func TestPurlWithTrailingSlash(t *testing.T) { - purl := "pkg:nuget/FluentUtils.FromCompositeAttribute/" - result := validate_purl(purl, testValidator) - expected := true +func TestErrorForInvalidPurl(t *testing.T) { + purl := "test:nuget/FluentUtils.FromCompositeAttribute" + _, err := validate_purl(purl, testValidator) - if result != expected { - t.Errorf("validate_purl(\"%s\") = %t; expected %t", purl, result, expected) + if err == nil { + t.Errorf("expected error but got nil") + } +} + +func TestErrorForUnsupportedPurl(t *testing.T) { + purl := "pkg:nuget/EnterpriseLibrary.Common@6.0.1304" + _, err := validate_purl(purl, testValidator) + + if err == nil { + t.Errorf("expected error but got nil") } } From 359e6b955f6fec6599abafdb5aeea7e279b8fad5 Mon Sep 17 00:00:00 2001 From: Keshav Priyadarshi Date: Wed, 11 Mar 2026 14:44:05 +0530 Subject: [PATCH 3/3] Add examples for invalid inputs Signed-off-by: Keshav Priyadarshi --- README.md | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f5ba270..08263e7 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![Version](https://img.shields.io/github/v/release/aboutcode-org/purlvalidator-go?style=for-the-badge)](https://github.com/aboutcode-org/purlvalidator-go/releases) [![Test](https://img.shields.io/github/actions/workflow/status/aboutcode-org/purlvalidator-go/ci.yml?style=for-the-badge&logo=github)](https://github.com/aboutcode-org/purlvalidator-go/actions) -**purlvalidator** is a Go library for validating [Package URLs (PURLs)](https://github.com/package-url/purl-spec). It works fully offline, including in **air-gapped** or **restricted environments**, and answers one key question: **Does the package this PURL represents actually exist?** +**purlvalidator** is a Go library for validating [Package-URLs (PURLs)](https://github.com/package-url/purl-spec). It works fully offline, including in **air-gapped** or **restricted environments**, and answers one key question: **Does the package this PURL represents actually exist?** ## How It Works? -**purlvalidator** is shipped with a pre-built FST (Finite State Transducer), a set of compact automata containing latest Package URLs mined by the MineCode[^1]. Library uses this FST to perform lookups and confirm whether the **base PURL**[^2] exists. +**purlvalidator** is shipped with a pre-built FST (Finite State Transducer), a set of compact automata containing latest Package-URLs mined by the MineCode[^1]. Library uses this FST to perform lookups and confirm whether the **base PURL**[^2] exists. ## Currently Supported Ecosystems @@ -30,15 +30,37 @@ Add `purlvalidator` as dependency in your go.mod ```bash -require github.com/aboutcode-org/purlvalidator-go v0.1.0 +require github.com/aboutcode-org/purlvalidator-go v1.0.0 ``` -Use it in your code like this +Use it in your code like this: -```rust +```go import "github.com/aboutcode-org/purlvalidator-go" -var result bool = purlvalidator.Validate("pkg:nuget/FluentValidation"); +func main() { + result, e := purlvalidator.Validate("pkg:nuget/FluentValidation"); + if err != nil { + panic(err) + } +} +``` + +Examples and errors: +```go +// This will return: true +purlvalidator.Validate("pkg:nuget/FluentValidation"); + +// This will return: false +purlvalidator.Validate("pkg:nuget/non-existent-foo-bar"); + + +// This will return an error: "only base PURL is supported (no version, qualifiers, or subpath)" +purlvalidator.Validate("pkg:nuget/FluentValidation@10.2.3"); + +// This will return an error: "purl scheme is not \"pkg\": \"pkddg\"" +purlvalidator.Validate("test:nuget/FluentValidation"); + ``` ## Contribution @@ -90,4 +112,4 @@ limitations under the License. ``` [^1]: MineCode continuously collects package metadata from various package ecosystems to maintain an up-to-date catalog of known packages. -[^2]: A Base Package URL is a Package URL without a version or subpath. +[^2]: A Base Package-URL is a Package-URL without a version, qualifiers or subpath.