mirror of
https://github.com/ossf/scorecard.git
synced 2024-11-05 05:17:00 +03:00
808941a4c2
* Token-Permissions, distinguish contents/package Allowing `contents: write` permission only for jobs that are releasing jobs, not just packaging jobs.
217 lines
5.6 KiB
Go
217 lines
5.6 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 checks
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/rhysd/actionlint"
|
|
|
|
"github.com/ossf/scorecard/v4/checker"
|
|
"github.com/ossf/scorecard/v4/checks/fileparser"
|
|
sce "github.com/ossf/scorecard/v4/errors"
|
|
)
|
|
|
|
// CheckPackaging is the registered name for Packaging.
|
|
const CheckPackaging = "Packaging"
|
|
|
|
//nolint:gochecknoinits
|
|
func init() {
|
|
if err := registerCheck(CheckPackaging, Packaging, nil); err != nil {
|
|
// this should never happen
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Packaging runs Packaging check.
|
|
func Packaging(c *checker.CheckRequest) checker.CheckResult {
|
|
matchedFiles, err := c.RepoClient.ListFiles(fileparser.IsGithubWorkflowFileCb)
|
|
if err != nil {
|
|
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("RepoClient.ListFiles: %v", err))
|
|
return checker.CreateRuntimeErrorResult(CheckPackaging, e)
|
|
}
|
|
|
|
for _, fp := range matchedFiles {
|
|
fc, err := c.RepoClient.GetFileContent(fp)
|
|
if err != nil {
|
|
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("RepoClient.GetFileContent: %v", err))
|
|
return checker.CreateRuntimeErrorResult(CheckPackaging, e)
|
|
}
|
|
|
|
workflow, errs := actionlint.Parse(fc)
|
|
if len(errs) > 0 && workflow == nil {
|
|
e := fileparser.FormatActionlintError(errs)
|
|
return checker.CreateRuntimeErrorResult(CheckPackaging, e)
|
|
}
|
|
if !isPackagingWorkflow(workflow, fp, c.Dlogger) {
|
|
continue
|
|
}
|
|
|
|
runs, err := c.RepoClient.ListSuccessfulWorkflowRuns(filepath.Base(fp))
|
|
if err != nil {
|
|
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("Client.Actions.ListWorkflowRunsByFileName: %v", err))
|
|
return checker.CreateRuntimeErrorResult(CheckPackaging, e)
|
|
}
|
|
if len(runs) > 0 {
|
|
c.Dlogger.Info(&checker.LogMessage{
|
|
Path: fp,
|
|
Type: checker.FileTypeSource,
|
|
Offset: checker.OffsetDefault,
|
|
Text: fmt.Sprintf("GitHub publishing workflow used in run %s", runs[0].URL),
|
|
})
|
|
return checker.CreateMaxScoreResult(CheckPackaging,
|
|
"publishing workflow detected")
|
|
}
|
|
c.Dlogger.Debug(&checker.LogMessage{
|
|
Path: fp,
|
|
Type: checker.FileTypeSource,
|
|
Offset: checker.OffsetDefault,
|
|
Text: "GitHub publishing workflow not used in runs",
|
|
})
|
|
}
|
|
|
|
c.Dlogger.Warn(&checker.LogMessage{
|
|
Text: "no GitHub publishing workflow detected",
|
|
})
|
|
|
|
return checker.CreateInconclusiveResult(CheckPackaging,
|
|
"no published package detected")
|
|
}
|
|
|
|
// A packaging workflow.
|
|
func isPackagingWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
|
|
jobMatchers := []fileparser.JobMatcher{
|
|
{
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "actions/setup-node",
|
|
With: map[string]string{"registry-url": "https://registry.npmjs.org"},
|
|
},
|
|
{
|
|
Run: "npm.*publish",
|
|
},
|
|
},
|
|
LogText: "candidate node publishing workflow using npm",
|
|
},
|
|
{
|
|
// Java packages with maven.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "actions/setup-java",
|
|
},
|
|
{
|
|
Run: "mvn.*deploy",
|
|
},
|
|
},
|
|
LogText: "candidate java publishing workflow using maven",
|
|
},
|
|
{
|
|
// Java packages with gradle.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "actions/setup-java",
|
|
},
|
|
{
|
|
Run: "gradle.*publish",
|
|
},
|
|
},
|
|
LogText: "candidate java publishing workflow using gradle",
|
|
},
|
|
{
|
|
// Ruby packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Run: "gem.*push",
|
|
},
|
|
},
|
|
LogText: "candidate ruby publishing workflow using gem",
|
|
},
|
|
{
|
|
// NuGet packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Run: "nuget.*push",
|
|
},
|
|
},
|
|
LogText: "candidate nuget publishing workflow",
|
|
},
|
|
{
|
|
// Docker packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Run: "docker.*push",
|
|
},
|
|
},
|
|
LogText: "candidate docker publishing workflow",
|
|
},
|
|
{
|
|
// Docker packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "docker/build-push-action",
|
|
},
|
|
},
|
|
LogText: "candidate docker publishing workflow",
|
|
},
|
|
{
|
|
// Python packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "actions/setup-python",
|
|
},
|
|
{
|
|
Uses: "pypa/gh-action-pypi-publish",
|
|
},
|
|
},
|
|
LogText: "candidate python publishing workflow using pypi",
|
|
},
|
|
{
|
|
// Python packages.
|
|
// This is a custom Python packaging workflow based on semantic versioning.
|
|
// TODO(#1642): accept custom workflows through a separate configuration.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "relekang/python-semantic-release",
|
|
},
|
|
},
|
|
LogText: "candidate python publishing workflow using python-semantic-release",
|
|
},
|
|
{
|
|
// Go packages.
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Uses: "actions/setup-go",
|
|
},
|
|
{
|
|
Uses: "goreleaser/goreleaser-action",
|
|
},
|
|
},
|
|
LogText: "candidate golang publishing workflow",
|
|
},
|
|
{
|
|
// Rust packages. https://doc.rust-lang.org/cargo/reference/publishing.html
|
|
Steps: []*fileparser.JobMatcherStep{
|
|
{
|
|
Run: "cargo.*publish",
|
|
},
|
|
},
|
|
LogText: "candidate rust publishing workflow using cargo",
|
|
},
|
|
}
|
|
|
|
return fileparser.AnyJobsMatch(workflow, jobMatchers, fp, dl, "not a publishing workflow")
|
|
}
|