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  }