github.com/google/osv-scalibr@v0.4.1/guidedremediation/upgrade/upgrade.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 upgrade provides the configuration for the allowable package upgrade levels for remediation. 16 package upgrade 17 18 import ( 19 "strings" 20 21 "deps.dev/util/semver" 22 ) 23 24 // Level is the maximum semver level of upgrade allowed for a package. 25 // i.e. if Level == Major, all upgrades are allowed, 26 // if Level == Minor, only upgrades up to minor (1.0.0 - 1.*.*) are allowed. 27 type Level int 28 29 // these constants represent the different allowable semver upgrade levels 30 const ( 31 Major Level = iota 32 Minor 33 Patch 34 None 35 ) 36 37 // Allows returns if the semver.Diff is allowable for this upgrade level constraint. 38 func (level Level) Allows(diff semver.Diff) bool { 39 if diff == semver.Same { 40 return true 41 } 42 43 switch level { 44 case Major: 45 return true 46 case Minor: 47 return diff != semver.DiffMajor 48 case Patch: 49 return (diff != semver.DiffMajor) && (diff != semver.DiffMinor) 50 case None: 51 return false 52 default: // Invalid level 53 return false 54 } 55 } 56 57 // Config hold the specified allowed Levels for each package in a manifest. 58 type Config map[string]Level 59 60 // NewConfig creates a new Config, with all packages allowing all upgrades. 61 func NewConfig() Config { 62 return make(Config) 63 } 64 65 // NewConfigFromStrings creates a new Config from a list of strings. 66 // Each string is in the format of "pkg:level", where pkg is the package name and level is one of 67 // "major", "minor", "patch", or "none". 68 // If pkg is not specified, the level is set as the default level for all packages not specified. 69 // Invalid strings are ignored, duplicate package strings are overwritten. 70 func NewConfigFromStrings(cfgStrings []string) Config { 71 cfg := NewConfig() 72 for _, c := range cfgStrings { 73 var pkg string 74 level := c 75 if idx := strings.LastIndex(c, ":"); idx != -1 { 76 pkg = c[:idx] 77 level = c[idx+1:] 78 } 79 switch level { 80 case "major": 81 cfg.Set(pkg, Major) 82 case "minor": 83 cfg.Set(pkg, Minor) 84 case "patch": 85 cfg.Set(pkg, Patch) 86 case "none": 87 cfg.Set(pkg, None) 88 // Ignore invalid levels. 89 } 90 } 91 92 return cfg 93 } 94 95 // Set the allowed upgrade level for a given pkg name. 96 // If level for pkg was previously set, sets the package to the new level and returns true. 97 // Otherwise, sets the package's level and returns false. 98 func (c Config) Set(pkg string, level Level) bool { 99 _, alreadySet := c[pkg] 100 c[pkg] = level 101 102 return alreadySet 103 } 104 105 // SetDefault sets the default allowed upgrade level packages that weren't explicitly set. 106 // If default was previously set, sets the default to the new level and returns true. 107 // Otherwise, sets the default and returns false. 108 func (c Config) SetDefault(level Level) bool { 109 // Empty package name is used as the default level. 110 return c.Set("", level) 111 } 112 113 // Get the allowed Level for the given pkg name. 114 func (c Config) Get(pkg string) Level { 115 if lvl, ok := c[pkg]; ok { 116 return lvl 117 } 118 119 // Empty package name is used as the default level. 120 return c[""] 121 }