github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/erlang/mixlock/mixlockutils/mixlockutils.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 mixlockutils provides common functions for parsing Mix.lock lockfiles. 16 package mixlockutils 17 18 import ( 19 "bufio" 20 "fmt" 21 "regexp" 22 23 "github.com/google/osv-scalibr/extractor" 24 "github.com/google/osv-scalibr/extractor/filesystem" 25 "github.com/google/osv-scalibr/inventory" 26 "github.com/google/osv-scalibr/log" 27 "github.com/google/osv-scalibr/purl" 28 ) 29 30 var ( 31 // "name": {:git, repo, "commit-hash", <other comma-separated values> }, 32 gitDependencyLineRe = regexp.MustCompile(`^ +"([^"]+)": \{:git, +"([^,]+)", +\"([^,]+)\",.+$`) 33 // "name": {source, name, "version", "commit-hash", <other comma-separated values> }, 34 regularDependencyLineRe = regexp.MustCompile(`^ +"([^"]+)": \{([^,]+), +([^,]+), +\"([^,]+)\", +\"([^,]+)\",.+$`) 35 ) 36 37 // Package represents a single package parsed from Mix.lock. 38 type Package struct { 39 Name string 40 Version string 41 Locations []string 42 SourceCode string 43 } 44 45 // ParseMixLockFile extracts packages from Erlang Mix.lock files passed through the scan input. 46 func ParseMixLockFile(input *filesystem.ScanInput) (inventory.Inventory, error) { 47 scanner := bufio.NewScanner(input.Reader) 48 49 packages := []*extractor.Package{} 50 51 for scanner.Scan() { 52 line := scanner.Text() 53 54 var name, version, commit string 55 56 // Matching git dependency line 57 if match := gitDependencyLineRe.FindStringSubmatch(line); match != nil { 58 // This is a git dependency line, doesn't have version info 59 if len(match) < 4 { 60 log.Errorf("invalid mix.lock dependency line %q", line) 61 continue 62 } 63 name = match[1] 64 commit = match[3] 65 version = "" // Git dependency doesn't have version info, so empty string 66 } else { 67 // This is a regular dependency line with both version and commit info 68 match := regularDependencyLineRe.FindStringSubmatch(line) 69 if match == nil { 70 continue 71 } 72 if len(match) < 6 { 73 log.Errorf("invalid mix.lock dependency line %q", line) 74 continue 75 } 76 name = match[1] 77 version = match[4] 78 commit = match[5] 79 } 80 81 // Directly appending to packages 82 packages = append(packages, &extractor.Package{ 83 Name: name, 84 Version: version, 85 PURLType: purl.TypeHex, 86 Locations: []string{input.Path}, 87 SourceCode: &extractor.SourceCodeIdentifier{ 88 Commit: commit, 89 }, 90 }) 91 } 92 93 if err := scanner.Err(); err != nil { 94 return inventory.Inventory{}, fmt.Errorf("error while scanning: %w", err) 95 } 96 97 return inventory.Inventory{Packages: packages}, nil 98 }