github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/elixir/mixlock/mixlock.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 mixlock extracts elixir mix.lock files. 16 package mixlock 17 18 import ( 19 "context" 20 "path/filepath" 21 22 "github.com/google/osv-scalibr/extractor/filesystem" 23 "github.com/google/osv-scalibr/extractor/filesystem/internal/units" 24 "github.com/google/osv-scalibr/extractor/filesystem/language/erlang/mixlock/mixlockutils" 25 "github.com/google/osv-scalibr/inventory" 26 "github.com/google/osv-scalibr/plugin" 27 "github.com/google/osv-scalibr/stats" 28 ) 29 30 const ( 31 // Name is the unique name of this extractor. 32 Name = "elixir/mixlock" 33 34 // defaultMaxFileSizeBytes is the maximum file size this extractor will process. 35 defaultMaxFileSizeBytes = 10 * units.MiB // 10 MB 36 ) 37 38 // Config is the configuration for the Elixir extractor. 39 type Config struct { 40 // Stats is a stats collector for reporting metrics. 41 Stats stats.Collector 42 // MaxFileSizeBytes is the maximum file size this extractor will unmarshal. If 43 // `FileRequired` gets a bigger file, it will return false, 44 MaxFileSizeBytes int64 45 } 46 47 // DefaultConfig returns the default configuration for the Elixir extractor. 48 func DefaultConfig() Config { 49 return Config{ 50 MaxFileSizeBytes: defaultMaxFileSizeBytes, 51 } 52 } 53 54 // Extractor structure for mix.lock files. 55 type Extractor struct { 56 stats stats.Collector 57 maxFileSizeBytes int64 58 } 59 60 // New returns an Elixir extractor. 61 // 62 // For most use cases, initialize with: 63 // ``` 64 // e := New(DefaultConfig()) 65 // ``` 66 func New(cfg Config) *Extractor { 67 return &Extractor{ 68 stats: cfg.Stats, 69 maxFileSizeBytes: cfg.MaxFileSizeBytes, 70 } 71 } 72 73 // NewDefault returns an extractor with the default config settings. 74 func NewDefault() filesystem.Extractor { return New(DefaultConfig()) } 75 76 // Config returns the configuration of the extractor. 77 func (e Extractor) Config() Config { 78 return Config{ 79 Stats: e.stats, 80 MaxFileSizeBytes: e.maxFileSizeBytes, 81 } 82 } 83 84 // Name of the extractor. 85 func (e Extractor) Name() string { return Name } 86 87 // Version of the extractor. 88 func (e Extractor) Version() int { return 0 } 89 90 // Requirements of the extractor. 91 func (e Extractor) Requirements() *plugin.Capabilities { return &plugin.Capabilities{} } 92 93 // FileRequired returns true if the specified file matches the mix.lock pattern. 94 func (e Extractor) FileRequired(api filesystem.FileAPI) bool { 95 path := api.Path() 96 if !(filepath.Base(path) == "mix.lock") { 97 return false 98 } 99 100 fileinfo, err := api.Stat() 101 if err != nil || (e.maxFileSizeBytes > 0 && fileinfo.Size() > e.maxFileSizeBytes) { 102 e.reportFileRequired(path, stats.FileRequiredResultSizeLimitExceeded) 103 return false 104 } 105 106 e.reportFileRequired(path, stats.FileRequiredResultOK) 107 return true 108 } 109 110 func (e Extractor) reportFileRequired(path string, result stats.FileRequiredResult) { 111 if e.stats == nil { 112 return 113 } 114 e.stats.AfterFileRequired(e.Name(), &stats.FileRequiredStats{ 115 Path: path, 116 Result: result, 117 }) 118 } 119 120 // Extract parses the mix.lock file to extract Elixir package dependencies. 121 func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) { 122 inv, err := mixlockutils.ParseMixLockFile(input) 123 if e.stats != nil { 124 var fileSizeBytes int64 125 if input.Info != nil { 126 fileSizeBytes = input.Info.Size() 127 } 128 e.stats.AfterFileExtracted(e.Name(), &stats.FileExtractedStats{ 129 Path: input.Path, 130 Result: filesystem.ExtractorErrorToFileExtractedResult(err), 131 FileSizeBytes: fileSizeBytes, 132 }) 133 } 134 return inv, err 135 }