mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-11 00:45:36 +03:00
https://github.com/ossf/scorecard/issues/1393 Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com>
219 lines
6.0 KiB
Go
219 lines
6.0 KiB
Go
// Copyright 2020 Security Scorecard Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package checker includes structs and functions used for running a check.
|
|
package checker
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
type (
|
|
// DetailType is the type of details.
|
|
DetailType int
|
|
// FileType is the type of a file.
|
|
FileType int
|
|
)
|
|
|
|
const (
|
|
// MaxResultScore is the best score that can be given by a check.
|
|
MaxResultScore = 10
|
|
// MinResultScore is the worst score that can be given by a check.
|
|
MinResultScore = 0
|
|
// InconclusiveResultScore is returned when no reliable information can be retrieved by a check.
|
|
InconclusiveResultScore = -1
|
|
|
|
// OffsetDefault is used if we can't determine the offset, for example when referencing a file but not a
|
|
// specific location in the file.
|
|
OffsetDefault = uint(1)
|
|
)
|
|
|
|
const (
|
|
// DetailInfo is info-level log.
|
|
DetailInfo DetailType = iota
|
|
// DetailWarn is warn log.
|
|
DetailWarn
|
|
// DetailDebug is debug log.
|
|
DetailDebug
|
|
)
|
|
|
|
const (
|
|
// FileTypeNone is a default, not defined.
|
|
// FileTypeNone must be `0`.
|
|
FileTypeNone FileType = iota
|
|
// FileTypeSource is for source code files.
|
|
FileTypeSource
|
|
// FileTypeBinary is for binary files.
|
|
FileTypeBinary
|
|
// FileTypeText is for text files.
|
|
FileTypeText
|
|
// FileTypeURL for URLs.
|
|
FileTypeURL
|
|
)
|
|
|
|
// CheckResult captures result from a check run.
|
|
// nolint:govet
|
|
type CheckResult struct {
|
|
Name string
|
|
Version int
|
|
Error error
|
|
Details []CheckDetail
|
|
Score int
|
|
Reason string
|
|
}
|
|
|
|
// Remediation represents a remediation.
|
|
type Remediation struct {
|
|
// Code snippet for humans.
|
|
Snippet string
|
|
// Diff for machines.
|
|
Diff string
|
|
// Help text for humans.
|
|
HelpText string
|
|
// Help text in markdown format for humans.
|
|
HelpMarkdown string
|
|
}
|
|
|
|
// CheckDetail contains information for each detail.
|
|
type CheckDetail struct {
|
|
Msg LogMessage
|
|
Type DetailType // Any of DetailWarn, DetailInfo, DetailDebug.
|
|
}
|
|
|
|
// LogMessage is a structure that encapsulates detail's information.
|
|
// This allows updating the definition easily.
|
|
// nolint:govet
|
|
type LogMessage struct {
|
|
Text string // A short string explaining why the detail was recorded/logged.
|
|
Path string // Fullpath to the file.
|
|
Type FileType // Type of file.
|
|
Offset uint // Offset in the file of Path (line for source/text files).
|
|
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
|
|
Snippet string // Snippet of code
|
|
Remediation *Remediation // Remediation information, if any.
|
|
}
|
|
|
|
// CreateProportionalScore creates a proportional score.
|
|
func CreateProportionalScore(success, total int) int {
|
|
if total == 0 {
|
|
return 0
|
|
}
|
|
|
|
return int(math.Min(float64(MaxResultScore*success/total), float64(MaxResultScore)))
|
|
}
|
|
|
|
// AggregateScores adds up all scores
|
|
// and normalizes the result.
|
|
// Each score contributes equally.
|
|
func AggregateScores(scores ...int) int {
|
|
n := float64(len(scores))
|
|
r := 0
|
|
for _, s := range scores {
|
|
r += s
|
|
}
|
|
return int(math.Floor(float64(r) / n))
|
|
}
|
|
|
|
// AggregateScoresWithWeight adds up all scores
|
|
// and normalizes the result.
|
|
func AggregateScoresWithWeight(scores map[int]int) int {
|
|
r := 0
|
|
ws := 0
|
|
for s, w := range scores {
|
|
r += s * w
|
|
ws += w
|
|
}
|
|
return int(math.Floor(float64(r) / float64(ws)))
|
|
}
|
|
|
|
// NormalizeReason - placeholder function if we want to update range of scores.
|
|
func NormalizeReason(reason string, score int) string {
|
|
return fmt.Sprintf("%v -- score normalized to %d", reason, score)
|
|
}
|
|
|
|
// CreateResultWithScore is used when
|
|
// the check runs without runtime errors and we want to assign a
|
|
// specific score.
|
|
func CreateResultWithScore(name, reason string, score int) CheckResult {
|
|
return CheckResult{
|
|
Name: name,
|
|
// Old structure.
|
|
// New structure.
|
|
Version: 2,
|
|
Error: nil,
|
|
Score: score,
|
|
Reason: reason,
|
|
}
|
|
}
|
|
|
|
// CreateProportionalScoreResult is used when
|
|
// the check runs without runtime errors and we assign a
|
|
// proportional score. This may be used if a check contains
|
|
// multiple tests and we want to assign a score proportional
|
|
// the the number of tests that succeeded.
|
|
func CreateProportionalScoreResult(name, reason string, b, t int) CheckResult {
|
|
score := CreateProportionalScore(b, t)
|
|
return CheckResult{
|
|
Name: name,
|
|
// Old structure.
|
|
// New structure.
|
|
Version: 2,
|
|
Error: nil,
|
|
Score: score,
|
|
Reason: NormalizeReason(reason, score),
|
|
}
|
|
}
|
|
|
|
// CreateMaxScoreResult is used when
|
|
// the check runs without runtime errors and we can assign a
|
|
// maximum score to the result.
|
|
func CreateMaxScoreResult(name, reason string) CheckResult {
|
|
return CreateResultWithScore(name, reason, MaxResultScore)
|
|
}
|
|
|
|
// CreateMinScoreResult is used when
|
|
// the check runs without runtime errors and we can assign a
|
|
// minimum score to the result.
|
|
func CreateMinScoreResult(name, reason string) CheckResult {
|
|
return CreateResultWithScore(name, reason, MinResultScore)
|
|
}
|
|
|
|
// CreateInconclusiveResult is used when
|
|
// the check runs without runtime errors, but we don't
|
|
// have enough evidence to set a score.
|
|
func CreateInconclusiveResult(name, reason string) CheckResult {
|
|
return CheckResult{
|
|
Name: name,
|
|
// Old structure.
|
|
// New structure.
|
|
Version: 2,
|
|
Score: InconclusiveResultScore,
|
|
Reason: reason,
|
|
}
|
|
}
|
|
|
|
// CreateRuntimeErrorResult is used when the check fails to run because of a runtime error.
|
|
func CreateRuntimeErrorResult(name string, e error) CheckResult {
|
|
return CheckResult{
|
|
Name: name,
|
|
// Old structure.
|
|
// New structure.
|
|
Version: 2,
|
|
Error: e,
|
|
Score: InconclusiveResultScore,
|
|
Reason: e.Error(), // Note: message already accessible by caller thru `Error`.
|
|
}
|
|
}
|