github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/nim/nimble/nimble.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package nimble extracts .nimble files from installed nimble packages. 16 package nimble 17 18 import ( 19 "context" 20 "path/filepath" 21 "regexp" 22 "strings" 23 24 "github.com/google/osv-scalibr/extractor" 25 "github.com/google/osv-scalibr/extractor/filesystem" 26 "github.com/google/osv-scalibr/inventory" 27 "github.com/google/osv-scalibr/log" 28 "github.com/google/osv-scalibr/plugin" 29 "github.com/google/osv-scalibr/purl" 30 ) 31 32 const ( 33 // Name is the unique name of this extractor. 34 Name = "nim/nimble" 35 // These two path slices can be used to determine Nimble package paths. 36 pkgsPath = "/pkgs/" 37 pkgs2Path = "/pkgs2/" 38 ) 39 40 // Extractor extracts nimble package info from .nimble files. 41 type Extractor struct{} 42 43 // New returns a nim nimble extractor. 44 func New() filesystem.Extractor { return &Extractor{} } 45 46 // Name of the extractor 47 func (e Extractor) Name() string { return Name } 48 49 // Version of the extractor 50 func (e Extractor) Version() int { return 0 } 51 52 // Requirements of the extractor. 53 func (e Extractor) Requirements() *plugin.Capabilities { return &plugin.Capabilities{} } 54 55 // FileRequired return true if the specified file matched the .nimble file pattern. 56 func (e Extractor) FileRequired(api filesystem.FileAPI) bool { 57 filePath := api.Path() 58 if !strings.HasSuffix(filePath, ".nimble") { 59 return false 60 } 61 if !(strings.Contains(filePath, pkgsPath) || strings.Contains(filePath, pkgs2Path)) { 62 return false 63 } 64 return true 65 } 66 67 // This regexp is used to extract packageName and version from the folder name for both new and old versioning ex: /root/.nimble/pkgs2/json_serialization-0.4.2-2b26a9e0fc79638dbb9272fb4ab5a1d79264f938 or /root/.nimble/pkgs/gura-0.1.1" 68 var reFolder = regexp.MustCompile(`^(.*?)-([0-9]+(?:\.[0-9]+)*)(?:-[a-f0-9]+)?$`) 69 70 // Extract extracts Package info from .Nimble file passed through the scan input. 71 func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) { 72 pkgs := e.extractFromPath(input.Path) 73 return inventory.Inventory{Packages: pkgs}, nil 74 } 75 76 func (e Extractor) extractFromPath(path string) []*extractor.Package { 77 // Get the first folder before nimble file 78 dirName := filepath.Base(filepath.Dir(path)) 79 match := reFolder.FindStringSubmatch(dirName) 80 81 if len(match) == 3 { 82 pkg := &extractor.Package{ 83 Name: match[1], 84 Version: match[2], 85 PURLType: purl.TypeNim, 86 Locations: []string{path}, 87 } 88 return []*extractor.Package{pkg} 89 } 90 91 log.Errorf("failed to extract package version from the following path : %s", path) 92 return nil 93 }