-
Notifications
You must be signed in to change notification settings - Fork 506
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ probe: releases with verified provenance (#4141)
* add projectpackageversions to signed releases raw results Signed-off-by: Raghav Kaul <[email protected]> * finding: add NewNot* helpers, fix error msg Signed-off-by: Raghav Kaul <[email protected]> * probe: releasesHaveVerifiedProvenance Signed-off-by: Raghav Kaul <[email protected]> * logging Signed-off-by: Raghav Kaul <[email protected]> * fix tests and lint Signed-off-by: Raghav Kaul <[email protected]> * address comments Signed-off-by: Raghav Kaul <[email protected]> * remove unused Signed-off-by: Raghav Kaul <[email protected]> * fix merge conflict Signed-off-by: Raghav Kaul <[email protected]> --------- Signed-off-by: Raghav Kaul <[email protected]>
- Loading branch information
1 parent
9cd1fb8
commit bfaa9fe
Showing
8 changed files
with
250 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Copyright 2024 OpenSSF 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. | ||
|
||
id: releasesHaveVerifiedProvenance | ||
short: Checks if the project releases with provenance attestations that have been verified | ||
motivation: > | ||
Package provenance attestations provide a greater guarantee of authenticity and integrity than package signatures alone, since the attestation can be performed over a hash of both the package contents and metadata. Developers can attest to particular qualities of the build, such as the build environment, build steps or builder identity. | ||
implementation: > | ||
This probe checks how many packages published by the repository are associated with verified SLSA provenance attestations. It uses data from a ProjectPackageClient, which associates a GitHub/GitLab project with a package in a package manager. Using the data from the package manager (whom we rely on to verify the provenance attestation), this probe returns a finding for each release. For now, only NPM is supported. | ||
outcome: | ||
- For each release, the probe returns OutcomeTrue or OutcomeFalse, depending on if the package has a verified provenance attestation. | ||
- If we didn't find a package or didn't find releases, return OutcomeNotAvailable. | ||
remediation: | ||
onOutcome: False | ||
effort: Low | ||
text: | ||
- For NPM, publish provenance alongside your package using the `--provenance` flag (See (Introducing npm package provenance)[https://github.blog/2023-04-19-introducing-npm-package-provenance/]) | ||
ecosystem: | ||
languages: | ||
- javascript | ||
clients: | ||
- github | ||
- gitlab |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2024 OpenSSF 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. | ||
|
||
//nolint:stylecheck | ||
package releasesHaveVerifiedProvenance | ||
|
||
import ( | ||
"embed" | ||
"fmt" | ||
|
||
"github.com/ossf/scorecard/v5/checker" | ||
"github.com/ossf/scorecard/v5/finding" | ||
"github.com/ossf/scorecard/v5/internal/probes" | ||
) | ||
|
||
func init() { | ||
probes.MustRegister(Probe, Run, []probes.CheckName{probes.SignedReleases}) | ||
} | ||
|
||
//go:embed *.yml | ||
var fs embed.FS | ||
|
||
const ( | ||
Probe = "releasesHaveVerifiedProvenance" | ||
) | ||
|
||
func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { | ||
var findings []finding.Finding | ||
|
||
if len(raw.SignedReleasesResults.Packages) == 0 { | ||
f, err := finding.NewNotApplicable(fs, Probe, "no package manager releases found", nil) | ||
if err != nil { | ||
return []finding.Finding{}, Probe, fmt.Errorf("create finding: %w", err) | ||
} | ||
findings = append(findings, *f) | ||
return findings, Probe, nil | ||
} | ||
|
||
for i := range raw.SignedReleasesResults.Packages { | ||
p := raw.SignedReleasesResults.Packages[i] | ||
|
||
if !p.Provenance.IsVerified { | ||
f, err := finding.NewFalse(fs, Probe, "release without verified provenance", nil) | ||
if err != nil { | ||
return []finding.Finding{}, Probe, fmt.Errorf("create finding: %w", err) | ||
} | ||
findings = append(findings, *f) | ||
continue | ||
} | ||
|
||
f, err := finding.NewTrue(fs, Probe, "release with verified provenance", nil) | ||
if err != nil { | ||
return []finding.Finding{}, Probe, fmt.Errorf("create finding: %w", err) | ||
} | ||
findings = append(findings, *f) | ||
} | ||
|
||
return findings, Probe, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// Copyright 2024 OpenSSF 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. | ||
|
||
//nolint:stylecheck | ||
package releasesHaveVerifiedProvenance | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/ossf/scorecard/v5/checker" | ||
"github.com/ossf/scorecard/v5/finding" | ||
) | ||
|
||
func Test_Run(t *testing.T) { | ||
t.Parallel() | ||
//nolint:govet | ||
tests := []struct { | ||
desc string | ||
pkgs []checker.ProjectPackage | ||
outcomes []finding.Outcome | ||
err error | ||
}{ | ||
{ | ||
desc: "no packages found", | ||
outcomes: []finding.Outcome{finding.OutcomeNotApplicable}, | ||
}, | ||
{ | ||
desc: "some releases with verified provenance", | ||
pkgs: []checker.ProjectPackage{ | ||
{ | ||
Name: "a", | ||
Version: "1.0.0", | ||
Provenance: checker.PackageProvenance{IsVerified: true}, | ||
}, | ||
{ | ||
Name: "a", | ||
Version: "1.0.1", | ||
}, | ||
}, | ||
outcomes: []finding.Outcome{finding.OutcomeTrue, finding.OutcomeFalse}, | ||
}, | ||
{ | ||
desc: "all releases with verified provenance", | ||
pkgs: []checker.ProjectPackage{ | ||
{ | ||
Name: "a", | ||
Version: "1.0.0", | ||
Provenance: checker.PackageProvenance{IsVerified: true}, | ||
}, | ||
{ | ||
Name: "a", | ||
Version: "1.0.1", | ||
Provenance: checker.PackageProvenance{IsVerified: true}, | ||
}, | ||
}, | ||
outcomes: []finding.Outcome{finding.OutcomeTrue, finding.OutcomeTrue}, | ||
}, | ||
{ | ||
desc: "no verified provenance", | ||
pkgs: []checker.ProjectPackage{ | ||
{ | ||
Name: "a", | ||
Version: "1.0.0", | ||
}, | ||
{ | ||
Name: "a", | ||
Version: "1.0.1", | ||
}, | ||
}, | ||
outcomes: []finding.Outcome{finding.OutcomeFalse, finding.OutcomeFalse}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
tt := tt // Re-initializing variable so it is not changed while executing the closure below | ||
t.Run(tt.desc, func(t *testing.T) { | ||
t.Parallel() | ||
raw := checker.RawResults{ | ||
SignedReleasesResults: checker.SignedReleasesData{ | ||
Packages: tt.pkgs, | ||
}, | ||
} | ||
|
||
outcomes, _, err := Run(&raw) | ||
|
||
if !errors.Is(tt.err, err) { | ||
t.Errorf("expected %+v got %+v", tt.err, err) | ||
} | ||
|
||
if !cmpOutcomes(tt.outcomes, outcomes) { | ||
t.Errorf("expected %+v got %+v", tt.outcomes, outcomes) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func cmpOutcomes(ex []finding.Outcome, act []finding.Finding) bool { | ||
if len(ex) != len(act) { | ||
return false | ||
} | ||
|
||
for i := range ex { | ||
if act[i].Outcome != ex[i] { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} |