mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-17 11:57:12 +03:00
✨ Raw result for Maintained check (#1780)
* draft * draft * raw results for Maintained check * updates * updates * missing files * updates * unit tests * e2e tests * tests * linter * updates
This commit is contained in:
parent
682e6ea176
commit
037a3f3516
@ -25,6 +25,15 @@ type RawResults struct {
|
|||||||
DependencyUpdateToolResults DependencyUpdateToolData
|
DependencyUpdateToolResults DependencyUpdateToolData
|
||||||
BranchProtectionResults BranchProtectionsData
|
BranchProtectionResults BranchProtectionsData
|
||||||
CodeReviewResults CodeReviewData
|
CodeReviewResults CodeReviewData
|
||||||
|
MaintainedResults MaintainedData
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaintainedData contains the raw results
|
||||||
|
// for the Maintained check.
|
||||||
|
type MaintainedData struct {
|
||||||
|
Issues []Issue
|
||||||
|
DefaultBranchCommits []DefaultBranchCommit
|
||||||
|
ArchivedStatus ArchivedStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeReviewData contains the raw results
|
// CodeReviewData contains the raw results
|
||||||
@ -107,9 +116,25 @@ type Run struct {
|
|||||||
// TODO: add fields, e.g., Result=["success", "failure"]
|
// TODO: add fields, e.g., Result=["success", "failure"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Comment represents a comment for a pull request or an issue.
|
||||||
|
type Comment struct {
|
||||||
|
CreatedAt *time.Time
|
||||||
|
Author *User
|
||||||
|
// TODO: add ields if needed, e.g., content.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArchivedStatus definess the archived status.
|
||||||
|
type ArchivedStatus struct {
|
||||||
|
Status bool
|
||||||
|
// TODO: add fields, e.g., date of archival.
|
||||||
|
}
|
||||||
|
|
||||||
// Issue represents an issue.
|
// Issue represents an issue.
|
||||||
type Issue struct {
|
type Issue struct {
|
||||||
URL string
|
CreatedAt *time.Time
|
||||||
|
Author *User
|
||||||
|
URL string
|
||||||
|
Comments []Comment
|
||||||
// TODO: add fields, e.g., state=[opened|closed]
|
// TODO: add fields, e.g., state=[opened|closed]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +146,7 @@ type DefaultBranchCommit struct {
|
|||||||
SHA string
|
SHA string
|
||||||
CommitMessage string
|
CommitMessage string
|
||||||
MergeRequest *MergeRequest
|
MergeRequest *MergeRequest
|
||||||
|
CommitDate *time.Time
|
||||||
Committer User
|
Committer User
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +169,32 @@ type Review struct {
|
|||||||
|
|
||||||
// User represent a user.
|
// User represent a user.
|
||||||
type User struct {
|
type User struct {
|
||||||
Login string
|
RepoAssociation *RepoAssociation
|
||||||
|
Login string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepoAssociation represents a user relationship with a repo.
|
||||||
|
type RepoAssociation string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RepoAssociationCollaborator has been invited to collaborate on the repository.
|
||||||
|
RepoAssociationCollaborator RepoAssociation = RepoAssociation("collaborator")
|
||||||
|
// RepoAssociationContributor is an contributor to the repository.
|
||||||
|
RepoAssociationContributor RepoAssociation = RepoAssociation("contributor")
|
||||||
|
// RepoAssociationOwner is an owner of the repository.
|
||||||
|
RepoAssociationOwner RepoAssociation = RepoAssociation("owner")
|
||||||
|
// RepoAssociationMember is a member of the organization that owns the repository.
|
||||||
|
RepoAssociationMember RepoAssociation = RepoAssociation("member")
|
||||||
|
// RepoAssociationFirstTimer has previously committed to the repository.
|
||||||
|
RepoAssociationFirstTimer RepoAssociation = RepoAssociation("first-timer")
|
||||||
|
// RepoAssociationFirstTimeContributor has not previously committed to the repository.
|
||||||
|
RepoAssociationFirstTimeContributor RepoAssociation = RepoAssociation("first-timer-contributor")
|
||||||
|
// RepoAssociationMannequin is a placeholder for an unclaimed user.
|
||||||
|
RepoAssociationMannequin RepoAssociation = RepoAssociation("unknown")
|
||||||
|
// RepoAssociationNone has no association with the repository.
|
||||||
|
RepoAssociationNone RepoAssociation = RepoAssociation("none")
|
||||||
|
)
|
||||||
|
|
||||||
// File represents a file.
|
// File represents a file.
|
||||||
type File struct {
|
type File struct {
|
||||||
Path string
|
Path string
|
||||||
|
@ -26,10 +26,10 @@ const (
|
|||||||
// CheckCIIBestPractices is the registered name for CIIBestPractices.
|
// CheckCIIBestPractices is the registered name for CIIBestPractices.
|
||||||
CheckCIIBestPractices = "CII-Best-Practices"
|
CheckCIIBestPractices = "CII-Best-Practices"
|
||||||
silverScore = 7
|
silverScore = 7
|
||||||
// Note: if this value is changed, please update the action's threshold score
|
// Note: if this value is changed, please update the action's threshold score
|
||||||
// https://github.com/ossf/scorecard-action/blob/main/policies/template.yml#L61.
|
// https://github.com/ossf/scorecard-action/blob/main/policies/template.yml#L61.
|
||||||
passingScore = 5
|
passingScore = 5
|
||||||
inProgressScore = 2
|
inProgressScore = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:gochecknoinits
|
//nolint:gochecknoinits
|
||||||
|
103
checks/evaluation/maintained.go
Normal file
103
checks/evaluation/maintained.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2021 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 evaluation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
sce "github.com/ossf/scorecard/v4/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lookBackDays = 90
|
||||||
|
activityPerWeek = 1
|
||||||
|
daysInOneWeek = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Maintained applies the score policy for the Maintained check.
|
||||||
|
func Maintained(name string, dl checker.DetailLogger, r *checker.MaintainedData) checker.CheckResult {
|
||||||
|
if r == nil {
|
||||||
|
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
|
||||||
|
return checker.CreateRuntimeErrorResult(name, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.ArchivedStatus.Status {
|
||||||
|
return checker.CreateMinScoreResult(name, "repo is marked as archived")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not explicitly marked archived, look for activity in past `lookBackDays`.
|
||||||
|
threshold := time.Now().AddDate(0 /*years*/, 0 /*months*/, -1*lookBackDays /*days*/)
|
||||||
|
commitsWithinThreshold := 0
|
||||||
|
for i := range r.DefaultBranchCommits {
|
||||||
|
if r.DefaultBranchCommits[i].CommitDate.After(threshold) {
|
||||||
|
commitsWithinThreshold++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issuesUpdatedWithinThreshold := 0
|
||||||
|
for i := range r.Issues {
|
||||||
|
if hasActivityByCollaboratorOrHigher(&r.Issues[i], threshold) {
|
||||||
|
issuesUpdatedWithinThreshold++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return checker.CreateProportionalScoreResult(name, fmt.Sprintf(
|
||||||
|
"%d commit(s) out of %d and %d issue activity out of %d found in the last %d days",
|
||||||
|
commitsWithinThreshold, len(r.DefaultBranchCommits), issuesUpdatedWithinThreshold, len(r.Issues), lookBackDays),
|
||||||
|
commitsWithinThreshold+issuesUpdatedWithinThreshold, activityPerWeek*lookBackDays/daysInOneWeek)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasActivityByCollaboratorOrHigher returns true if the issue was created or commented on by an
|
||||||
|
// owner/collaborator/member since the threshold.
|
||||||
|
func hasActivityByCollaboratorOrHigher(issue *checker.Issue, threshold time.Time) bool {
|
||||||
|
if issue == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if isCollaboratorOrHigher(issue.Author) && issue.CreatedAt != nil && issue.CreatedAt.After(threshold) {
|
||||||
|
// The creator of the issue is a collaborator or higher.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, comment := range issue.Comments {
|
||||||
|
if isCollaboratorOrHigher(comment.Author) && comment.CreatedAt != nil &&
|
||||||
|
comment.CreatedAt.After(threshold) {
|
||||||
|
// The author of the comment is a collaborator or higher.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCollaboratorOrHigher returns true if the user is a collaborator or higher.
|
||||||
|
func isCollaboratorOrHigher(user *checker.User) bool {
|
||||||
|
if user == nil || user.RepoAssociation == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
priviledgedRoles := []checker.RepoAssociation{
|
||||||
|
checker.RepoAssociationOwner,
|
||||||
|
checker.RepoAssociationCollaborator,
|
||||||
|
checker.RepoAssociationContributor,
|
||||||
|
checker.RepoAssociationMember,
|
||||||
|
}
|
||||||
|
for _, role := range priviledgedRoles {
|
||||||
|
if role == *user.RepoAssociation {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -15,108 +15,35 @@
|
|||||||
package checks
|
package checks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ossf/scorecard/v4/checker"
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
"github.com/ossf/scorecard/v4/clients"
|
"github.com/ossf/scorecard/v4/checks/evaluation"
|
||||||
|
"github.com/ossf/scorecard/v4/checks/raw"
|
||||||
sce "github.com/ossf/scorecard/v4/errors"
|
sce "github.com/ossf/scorecard/v4/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// CheckMaintained is the exported check name for Maintained.
|
||||||
// CheckMaintained is the exported check name for Maintained.
|
const CheckMaintained = "Maintained"
|
||||||
CheckMaintained = "Maintained"
|
|
||||||
lookBackDays = 90
|
|
||||||
activityPerWeek = 1
|
|
||||||
daysInOneWeek = 7
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:gochecknoinits
|
//nolint:gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
if err := registerCheck(CheckMaintained, IsMaintained, nil); err != nil {
|
if err := registerCheck(CheckMaintained, Maintained, nil); err != nil {
|
||||||
// this should never happen
|
// this should never happen
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsMaintained runs Maintained check.
|
// Maintained runs Maintained check.
|
||||||
func IsMaintained(c *checker.CheckRequest) checker.CheckResult {
|
func Maintained(c *checker.CheckRequest) checker.CheckResult {
|
||||||
archived, err := c.RepoClient.IsArchived()
|
rawData, err := raw.Maintained(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||||
return checker.CreateRuntimeErrorResult(CheckMaintained, e)
|
return checker.CreateRuntimeErrorResult(CheckMaintained, e)
|
||||||
}
|
}
|
||||||
if archived {
|
|
||||||
return checker.CreateMinScoreResult(CheckMaintained, "repo is marked as archived")
|
// Set the raw results.
|
||||||
|
if c.RawResults != nil {
|
||||||
|
c.RawResults.MaintainedResults = rawData
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not explicitly marked archived, look for activity in past `lookBackDays`.
|
return evaluation.Maintained(CheckMaintained, c.Dlogger, &rawData)
|
||||||
threshold := time.Now().AddDate(0 /*years*/, 0 /*months*/, -1*lookBackDays /*days*/)
|
|
||||||
|
|
||||||
commits, err := c.RepoClient.ListCommits()
|
|
||||||
if err != nil {
|
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
|
||||||
return checker.CreateRuntimeErrorResult(CheckMaintained, e)
|
|
||||||
}
|
|
||||||
commitsWithinThreshold := 0
|
|
||||||
for i := range commits {
|
|
||||||
if commits[i].CommittedDate.After(threshold) {
|
|
||||||
commitsWithinThreshold++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issues, err := c.RepoClient.ListIssues()
|
|
||||||
if err != nil {
|
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
|
||||||
return checker.CreateRuntimeErrorResult(CheckMaintained, e)
|
|
||||||
}
|
|
||||||
issuesUpdatedWithinThreshold := 0
|
|
||||||
for i := range issues {
|
|
||||||
if hasActivityByCollaboratorOrHigher(&issues[i], threshold) {
|
|
||||||
issuesUpdatedWithinThreshold++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return checker.CreateProportionalScoreResult(CheckMaintained, fmt.Sprintf(
|
|
||||||
"%d commit(s) out of %d and %d issue activity out of %d found in the last %d days",
|
|
||||||
commitsWithinThreshold, len(commits), issuesUpdatedWithinThreshold, len(issues), lookBackDays),
|
|
||||||
commitsWithinThreshold+issuesUpdatedWithinThreshold, activityPerWeek*lookBackDays/daysInOneWeek)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasActivityByCollaboratorOrHigher returns true if the issue was created or commented on by an
|
|
||||||
// owner/collaborator/member since the threshold.
|
|
||||||
func hasActivityByCollaboratorOrHigher(issue *clients.Issue, threshold time.Time) bool {
|
|
||||||
if issue == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if isCollaboratorOrHigher(issue.AuthorAssociation) && issue.CreatedAt != nil && issue.CreatedAt.After(threshold) {
|
|
||||||
// The creator of the issue is a collaborator or higher.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, comment := range issue.Comments {
|
|
||||||
if isCollaboratorOrHigher(comment.AuthorAssociation) && comment.CreatedAt != nil &&
|
|
||||||
comment.CreatedAt.After(threshold) {
|
|
||||||
// The author of the comment is a collaborator or higher.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isCollaboratorOrHigher returns true if the user is a collaborator or higher.
|
|
||||||
func isCollaboratorOrHigher(repoAssociation *clients.RepoAssociation) bool {
|
|
||||||
if repoAssociation == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
priviledgedRoles := []clients.RepoAssociation{
|
|
||||||
clients.RepoAssociationCollaborator,
|
|
||||||
clients.RepoAssociationMember,
|
|
||||||
clients.RepoAssociationOwner,
|
|
||||||
}
|
|
||||||
for _, role := range priviledgedRoles {
|
|
||||||
if role == *repoAssociation {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,10 @@ import (
|
|||||||
scut "github.com/ossf/scorecard/v4/utests"
|
scut "github.com/ossf/scorecard/v4/utests"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: gocognit
|
|
||||||
// ignoring the linter for cyclomatic complexity because it is a test func
|
// ignoring the linter for cyclomatic complexity because it is a test func
|
||||||
// TestMaintained tests the maintained check.
|
// TestMaintained tests the maintained check.
|
||||||
func TestMaintained(t *testing.T) {
|
//nolint
|
||||||
|
func Test_Maintained(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
threeHundredDaysAgo := time.Now().AddDate(0, 0, -300)
|
threeHundredDaysAgo := time.Now().AddDate(0, 0, -300)
|
||||||
twoHundredDaysAgo := time.Now().AddDate(0, 0, -200)
|
twoHundredDaysAgo := time.Now().AddDate(0, 0, -200)
|
||||||
@ -38,7 +38,13 @@ func TestMaintained(t *testing.T) {
|
|||||||
oneDayAgo := time.Now().AddDate(0, 0, -1)
|
oneDayAgo := time.Now().AddDate(0, 0, -1)
|
||||||
ownerAssociation := clients.RepoAssociationOwner
|
ownerAssociation := clients.RepoAssociationOwner
|
||||||
noneAssociation := clients.RepoAssociationNone
|
noneAssociation := clients.RepoAssociationNone
|
||||||
//fieldalignment lint issue. Ignoring it as it is not important for this test.
|
// fieldalignment lint issue. Ignoring it as it is not important for this test.
|
||||||
|
someone := clients.User{
|
||||||
|
Login: "someone",
|
||||||
|
}
|
||||||
|
otheruser := clients.User{
|
||||||
|
Login: "someone-else",
|
||||||
|
}
|
||||||
//nolint
|
//nolint
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
err error
|
err error
|
||||||
@ -85,7 +91,6 @@ func TestMaintained(t *testing.T) {
|
|||||||
Score: -1,
|
Score: -1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "repo with no commits or issues",
|
name: "repo with no commits or issues",
|
||||||
isarchived: false,
|
isarchived: false,
|
||||||
@ -125,10 +130,12 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &threeHundredDaysAgo,
|
CreatedAt: &threeHundredDaysAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
CreatedAt: &twoHundredDaysAgo,
|
CreatedAt: &twoHundredDaysAgo,
|
||||||
AuthorAssociation: &noneAssociation,
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: checker.CheckResult{
|
expected: checker.CheckResult{
|
||||||
@ -143,10 +150,12 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &fiveDaysAgo,
|
CreatedAt: &fiveDaysAgo,
|
||||||
AuthorAssociation: &noneAssociation,
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
CreatedAt: &oneDayAgo,
|
CreatedAt: &oneDayAgo,
|
||||||
AuthorAssociation: &noneAssociation,
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: checker.CheckResult{
|
expected: checker.CheckResult{
|
||||||
@ -165,6 +174,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &oneDayAgo,
|
CreatedAt: &oneDayAgo,
|
||||||
AuthorAssociation: &noneAssociation,
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -175,6 +185,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &oneDayAgo,
|
CreatedAt: &oneDayAgo,
|
||||||
AuthorAssociation: &noneAssociation,
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -195,6 +206,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &twoHundredDaysAgo,
|
CreatedAt: &twoHundredDaysAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -205,6 +217,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &twoHundredDaysAgo,
|
CreatedAt: &twoHundredDaysAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -225,6 +238,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &fiveDaysAgo,
|
CreatedAt: &fiveDaysAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -235,6 +249,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &oneDayAgo,
|
CreatedAt: &oneDayAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -251,16 +266,38 @@ func TestMaintained(t *testing.T) {
|
|||||||
{
|
{
|
||||||
CreatedAt: &fiveDaysAgo,
|
CreatedAt: &fiveDaysAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
CreatedAt: &oneDayAgo,
|
CreatedAt: &oneDayAgo,
|
||||||
AuthorAssociation: &ownerAssociation,
|
AuthorAssociation: &ownerAssociation,
|
||||||
|
Author: &someone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: checker.CheckResult{
|
expected: checker.CheckResult{
|
||||||
Score: 1,
|
Score: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "new issues by non-owner",
|
||||||
|
isarchived: false,
|
||||||
|
commits: []clients.Commit{},
|
||||||
|
issues: []clients.Issue{
|
||||||
|
{
|
||||||
|
CreatedAt: &fiveDaysAgo,
|
||||||
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &otheruser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CreatedAt: &oneDayAgo,
|
||||||
|
AuthorAssociation: &noneAssociation,
|
||||||
|
Author: &otheruser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: checker.CheckResult{
|
||||||
|
Score: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -279,7 +316,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
return tt.isarchived, nil
|
return tt.isarchived, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if !tt.isarchived {
|
if tt.archiveerr == nil {
|
||||||
mockRepo.EXPECT().ListCommits().DoAndReturn(
|
mockRepo.EXPECT().ListCommits().DoAndReturn(
|
||||||
func() ([]clients.Commit, error) {
|
func() ([]clients.Commit, error) {
|
||||||
if tt.commiterr != nil {
|
if tt.commiterr != nil {
|
||||||
@ -288,6 +325,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
return tt.commits, tt.err
|
return tt.commits, tt.err
|
||||||
},
|
},
|
||||||
).MinTimes(1)
|
).MinTimes(1)
|
||||||
|
|
||||||
if tt.commiterr == nil {
|
if tt.commiterr == nil {
|
||||||
mockRepo.EXPECT().ListIssues().DoAndReturn(
|
mockRepo.EXPECT().ListIssues().DoAndReturn(
|
||||||
func() ([]clients.Issue, error) {
|
func() ([]clients.Issue, error) {
|
||||||
@ -304,7 +342,7 @@ func TestMaintained(t *testing.T) {
|
|||||||
RepoClient: mockRepo,
|
RepoClient: mockRepo,
|
||||||
}
|
}
|
||||||
req.Dlogger = &scut.TestDetailLogger{}
|
req.Dlogger = &scut.TestDetailLogger{}
|
||||||
res := IsMaintained(&req)
|
res := Maintained(&req)
|
||||||
|
|
||||||
if tt.err != nil {
|
if tt.err != nil {
|
||||||
if res.Error2 == nil {
|
if res.Error2 == nil {
|
||||||
|
@ -32,19 +32,20 @@ func CodeReview(c clients.RepoClient) (checker.CodeReviewData, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range commits {
|
for i := range commits {
|
||||||
results = append(results, getRawDataFrom(&commits[i]))
|
results = append(results, getRawDataFromCommit(&commits[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return checker.CodeReviewData{DefaultBranchCommits: results}, nil
|
return checker.CodeReviewData{DefaultBranchCommits: results}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRawDataFrom(c *clients.Commit) checker.DefaultBranchCommit {
|
func getRawDataFromCommit(c *clients.Commit) checker.DefaultBranchCommit {
|
||||||
r := checker.DefaultBranchCommit{
|
r := checker.DefaultBranchCommit{
|
||||||
Committer: checker.User{
|
Committer: checker.User{
|
||||||
Login: c.Committer.Login,
|
Login: c.Committer.Login,
|
||||||
},
|
},
|
||||||
SHA: c.SHA,
|
SHA: c.SHA,
|
||||||
CommitMessage: c.Message,
|
CommitMessage: c.Message,
|
||||||
|
CommitDate: &c.CommittedDate,
|
||||||
MergeRequest: mergeRequest(&c.AssociatedMergeRequest),
|
MergeRequest: mergeRequest(&c.AssociatedMergeRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +191,8 @@ func Test_mergeRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test_getRawDataFrom tests the getRawDataFrom function.
|
// Test_getRawDataFromCommit tests the getRawDataFromCommit function.
|
||||||
func Test_getRawDataFrom(t *testing.T) {
|
func Test_getRawDataFromCommit(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
type args struct {
|
type args struct {
|
||||||
c *clients.Commit
|
c *clients.Commit
|
||||||
@ -203,7 +203,7 @@ func Test_getRawDataFrom(t *testing.T) {
|
|||||||
want checker.DefaultBranchCommit
|
want checker.DefaultBranchCommit
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Test_getRawDataFrom",
|
name: "Test_getRawDataFromCommit",
|
||||||
args: args{
|
args: args{
|
||||||
c: &clients.Commit{
|
c: &clients.Commit{
|
||||||
CommittedDate: time.Time{},
|
CommittedDate: time.Time{},
|
||||||
@ -214,6 +214,7 @@ func Test_getRawDataFrom(t *testing.T) {
|
|||||||
want: checker.DefaultBranchCommit{
|
want: checker.DefaultBranchCommit{
|
||||||
SHA: "sha",
|
SHA: "sha",
|
||||||
CommitMessage: "message",
|
CommitMessage: "message",
|
||||||
|
CommitDate: &time.Time{},
|
||||||
MergeRequest: &checker.MergeRequest{
|
MergeRequest: &checker.MergeRequest{
|
||||||
Labels: []string{},
|
Labels: []string{},
|
||||||
Reviews: []checker.Review{},
|
Reviews: []checker.Review{},
|
||||||
@ -226,7 +227,7 @@ func Test_getRawDataFrom(t *testing.T) {
|
|||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
if got := getRawDataFrom(tt.args.c); !cmp.Equal(got, tt.want) {
|
if got := getRawDataFromCommit(tt.args.c); !cmp.Equal(got, tt.want) {
|
||||||
t.Errorf(cmp.Diff(got, tt.want))
|
t.Errorf(cmp.Diff(got, tt.want))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
124
checks/raw/maintained.go
Normal file
124
checks/raw/maintained.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 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 raw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/clients"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Maintained checks for maintenance.
|
||||||
|
func Maintained(c *checker.CheckRequest) (checker.MaintainedData, error) {
|
||||||
|
var result checker.MaintainedData
|
||||||
|
|
||||||
|
// Archived status.
|
||||||
|
archived, err := c.RepoClient.IsArchived()
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
result.ArchivedStatus.Status = archived
|
||||||
|
|
||||||
|
// Recent commits.
|
||||||
|
commits, err := c.RepoClient.ListCommits()
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range commits {
|
||||||
|
// Note: getRawDataFromCommit() is defined in Code-Review check.
|
||||||
|
result.DefaultBranchCommits = append(result.DefaultBranchCommits,
|
||||||
|
getRawDataFromCommit(&commits[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recent issues.
|
||||||
|
issues, err := c.RepoClient.ListIssues()
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range issues {
|
||||||
|
// Create issue.
|
||||||
|
issue := checker.Issue{
|
||||||
|
CreatedAt: issues[i].CreatedAt,
|
||||||
|
}
|
||||||
|
// Add author if not nil.
|
||||||
|
if issues[i].Author != nil {
|
||||||
|
issue.Author = &checker.User{
|
||||||
|
Login: issues[i].Author.Login,
|
||||||
|
RepoAssociation: getAssociation(issues[i].AuthorAssociation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add URL if not nil.
|
||||||
|
if issues[i].URI != nil {
|
||||||
|
issue.URL = *issues[i].URI
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add comments.
|
||||||
|
for j := range issues[i].Comments {
|
||||||
|
comment := checker.Comment{
|
||||||
|
CreatedAt: issues[i].Comments[j].CreatedAt,
|
||||||
|
}
|
||||||
|
if issues[i].Comments[j].Author != nil {
|
||||||
|
comment.Author = &checker.User{
|
||||||
|
Login: issues[i].Comments[j].Author.Login,
|
||||||
|
RepoAssociation: getAssociation(issues[i].Comments[j].AuthorAssociation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue.Comments = append(issue.Comments, comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Issues = append(result.Issues, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAssociation(a *clients.RepoAssociation) *checker.RepoAssociation {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *a {
|
||||||
|
case clients.RepoAssociationContributor:
|
||||||
|
v := checker.RepoAssociationContributor
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationCollaborator:
|
||||||
|
v := checker.RepoAssociationCollaborator
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationOwner:
|
||||||
|
v := checker.RepoAssociationOwner
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationMember:
|
||||||
|
v := checker.RepoAssociationMember
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationFirstTimer:
|
||||||
|
v := checker.RepoAssociationFirstTimer
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationMannequin:
|
||||||
|
v := checker.RepoAssociationMannequin
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationNone:
|
||||||
|
v := checker.RepoAssociationNone
|
||||||
|
return &v
|
||||||
|
case clients.RepoAssociationFirstTimeContributor:
|
||||||
|
v := checker.RepoAssociationFirstTimeContributor
|
||||||
|
return &v
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -100,11 +100,17 @@ type graphqlData struct {
|
|||||||
// nolint: revive,stylecheck // naming according to githubv4 convention.
|
// nolint: revive,stylecheck // naming according to githubv4 convention.
|
||||||
Url *string
|
Url *string
|
||||||
AuthorAssociation *string
|
AuthorAssociation *string
|
||||||
CreatedAt *time.Time
|
Author struct {
|
||||||
Comments struct {
|
Login githubv4.String
|
||||||
|
}
|
||||||
|
CreatedAt *time.Time
|
||||||
|
Comments struct {
|
||||||
Nodes []struct {
|
Nodes []struct {
|
||||||
AuthorAssociation *string
|
AuthorAssociation *string
|
||||||
CreatedAt *time.Time
|
CreatedAt *time.Time
|
||||||
|
Author struct {
|
||||||
|
Login githubv4.String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} `graphql:"comments(last: $issueCommentsToAnalyze)"`
|
} `graphql:"comments(last: $issueCommentsToAnalyze)"`
|
||||||
}
|
}
|
||||||
@ -265,10 +271,20 @@ func issuesFrom(data *graphqlData) []clients.Issue {
|
|||||||
copyStringPtr(issue.Url, &tmpIssue.URI)
|
copyStringPtr(issue.Url, &tmpIssue.URI)
|
||||||
copyRepoAssociationPtr(getRepoAssociation(issue.AuthorAssociation), &tmpIssue.AuthorAssociation)
|
copyRepoAssociationPtr(getRepoAssociation(issue.AuthorAssociation), &tmpIssue.AuthorAssociation)
|
||||||
copyTimePtr(issue.CreatedAt, &tmpIssue.CreatedAt)
|
copyTimePtr(issue.CreatedAt, &tmpIssue.CreatedAt)
|
||||||
|
if issue.Author.Login != "" {
|
||||||
|
tmpIssue.Author = &clients.User{
|
||||||
|
Login: string(issue.Author.Login),
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, comment := range issue.Comments.Nodes {
|
for _, comment := range issue.Comments.Nodes {
|
||||||
var tmpComment clients.IssueComment
|
var tmpComment clients.IssueComment
|
||||||
copyRepoAssociationPtr(getRepoAssociation(comment.AuthorAssociation), &tmpComment.AuthorAssociation)
|
copyRepoAssociationPtr(getRepoAssociation(comment.AuthorAssociation), &tmpComment.AuthorAssociation)
|
||||||
copyTimePtr(comment.CreatedAt, &tmpComment.CreatedAt)
|
copyTimePtr(comment.CreatedAt, &tmpComment.CreatedAt)
|
||||||
|
if comment.Author.Login != "" {
|
||||||
|
tmpComment.Author = &clients.User{
|
||||||
|
Login: string(comment.Author.Login),
|
||||||
|
}
|
||||||
|
}
|
||||||
tmpIssue.Comments = append(tmpIssue.Comments, tmpComment)
|
tmpIssue.Comments = append(tmpIssue.Comments, tmpComment)
|
||||||
}
|
}
|
||||||
ret = append(ret, tmpIssue)
|
ret = append(ret, tmpIssue)
|
||||||
|
@ -20,6 +20,7 @@ import "time"
|
|||||||
type Issue struct {
|
type Issue struct {
|
||||||
URI *string
|
URI *string
|
||||||
CreatedAt *time.Time
|
CreatedAt *time.Time
|
||||||
|
Author *User
|
||||||
AuthorAssociation *RepoAssociation
|
AuthorAssociation *RepoAssociation
|
||||||
Comments []IssueComment
|
Comments []IssueComment
|
||||||
}
|
}
|
||||||
@ -27,5 +28,6 @@ type Issue struct {
|
|||||||
// IssueComment represents a comment on an issue.
|
// IssueComment represents a comment on an issue.
|
||||||
type IssueComment struct {
|
type IssueComment struct {
|
||||||
CreatedAt *time.Time
|
CreatedAt *time.Time
|
||||||
|
Author *User
|
||||||
AuthorAssociation *RepoAssociation
|
AuthorAssociation *RepoAssociation
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package mockrepo
|
package mockrepo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
@ -154,8 +155,11 @@ func (mr *MockRepoClientMockRecorder) ListCheckRunsForRef(ref interface{}) *gomo
|
|||||||
|
|
||||||
// ListCommits mocks base method.
|
// ListCommits mocks base method.
|
||||||
func (m *MockRepoClient) ListCommits() ([]clients.Commit, error) {
|
func (m *MockRepoClient) ListCommits() ([]clients.Commit, error) {
|
||||||
|
fmt.Println("mock.ListCommits")
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
fmt.Println("call mock.ListCommits")
|
||||||
ret := m.ctrl.Call(m, "ListCommits")
|
ret := m.ctrl.Call(m, "ListCommits")
|
||||||
|
fmt.Println("ret mock.ListCommits")
|
||||||
ret0, _ := ret[0].([]clients.Commit)
|
ret0, _ := ret[0].([]clients.Commit)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
@ -163,6 +167,7 @@ func (m *MockRepoClient) ListCommits() ([]clients.Commit, error) {
|
|||||||
|
|
||||||
// ListCommits indicates an expected call of ListCommits.
|
// ListCommits indicates an expected call of ListCommits.
|
||||||
func (mr *MockRepoClientMockRecorder) ListCommits() *gomock.Call {
|
func (mr *MockRepoClientMockRecorder) ListCommits() *gomock.Call {
|
||||||
|
fmt.Println("recorder.ListCommits")
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockRepoClient)(nil).ListCommits))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockRepoClient)(nil).ListCommits))
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ var _ = Describe("E2E TEST:"+checks.CheckMaintained, func() {
|
|||||||
NumberOfInfo: 0,
|
NumberOfInfo: 0,
|
||||||
NumberOfDebug: 0,
|
NumberOfDebug: 0,
|
||||||
}
|
}
|
||||||
result := checks.IsMaintained(&req)
|
result := checks.Maintained(&req)
|
||||||
// UPGRADEv2: to remove.
|
// UPGRADEv2: to remove.
|
||||||
// Old version.
|
// Old version.
|
||||||
Expect(result.Error).Should(BeNil())
|
Expect(result.Error).Should(BeNil())
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ossf/scorecard/v4/checker"
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
sce "github.com/ossf/scorecard/v4/errors"
|
sce "github.com/ossf/scorecard/v4/errors"
|
||||||
@ -70,7 +71,8 @@ type jsonReview struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type jsonUser struct {
|
type jsonUser struct {
|
||||||
Login string `json:"login"`
|
RepoAssociation *string `json:"repo-association"`
|
||||||
|
Login string `json:"login"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:govet
|
//nolint:govet
|
||||||
@ -98,7 +100,29 @@ type jsonDatabaseVulnerability struct {
|
|||||||
// TODO: additional information
|
// TODO: additional information
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonArchivedStatus struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
// TODO: add fields, e.g. date of archival, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonComment struct {
|
||||||
|
CreatedAt *time.Time `json:"created-at"`
|
||||||
|
Author *jsonUser `json:"author"`
|
||||||
|
// TODO: add ields if needed, e.g., content.
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonIssue struct {
|
||||||
|
CreatedAt *time.Time `json:"created-at"`
|
||||||
|
Author *jsonUser `json:"author"`
|
||||||
|
URL string `json:"URL"`
|
||||||
|
Comments []jsonComment `json:"comments"`
|
||||||
|
// TODO: add fields, e.g., state=[opened|closed]
|
||||||
|
}
|
||||||
|
|
||||||
type jsonRawResults struct {
|
type jsonRawResults struct {
|
||||||
|
// List of recent issues.
|
||||||
|
RecentIssues []jsonIssue `json:"issues"`
|
||||||
|
// List of vulnerabilities.
|
||||||
DatabaseVulnerabilities []jsonDatabaseVulnerability `json:"database-vulnerabilities"`
|
DatabaseVulnerabilities []jsonDatabaseVulnerability `json:"database-vulnerabilities"`
|
||||||
// List of binaries found in the repo.
|
// List of binaries found in the repo.
|
||||||
Binaries []jsonFile `json:"binaries"`
|
Binaries []jsonFile `json:"binaries"`
|
||||||
@ -112,15 +136,76 @@ type jsonRawResults struct {
|
|||||||
BranchProtections []jsonBranchProtection `json:"branch-protections"`
|
BranchProtections []jsonBranchProtection `json:"branch-protections"`
|
||||||
// Commits.
|
// Commits.
|
||||||
DefaultBranchCommits []jsonDefaultBranchCommit `json:"default-branch-commits"`
|
DefaultBranchCommits []jsonDefaultBranchCommit `json:"default-branch-commits"`
|
||||||
|
// Archived status of the repo.
|
||||||
|
ArchivedStatus jsonArchivedStatus `json:"archived"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unparam
|
func getRepoAssociation(author *checker.User) *string {
|
||||||
func (r *jsonScorecardRawResult) addCodeReviewRawResults(cr *checker.CodeReviewData) error {
|
if author == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if author.RepoAssociation == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s := string(*author.RepoAssociation)
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *jsonScorecardRawResult) addMaintainedRawResults(mr *checker.MaintainedData) error {
|
||||||
|
// Set archived status.
|
||||||
|
r.Results.ArchivedStatus = jsonArchivedStatus{Status: mr.ArchivedStatus.Status}
|
||||||
|
|
||||||
|
// Issues.
|
||||||
|
for i := range mr.Issues {
|
||||||
|
issue := jsonIssue{
|
||||||
|
CreatedAt: mr.Issues[i].CreatedAt,
|
||||||
|
URL: mr.Issues[i].URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
if mr.Issues[i].Author != nil {
|
||||||
|
issue.Author = &jsonUser{
|
||||||
|
Login: mr.Issues[i].Author.Login,
|
||||||
|
RepoAssociation: getRepoAssociation(mr.Issues[i].Author),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range mr.Issues[i].Comments {
|
||||||
|
comment := jsonComment{
|
||||||
|
CreatedAt: mr.Issues[i].Comments[j].CreatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if mr.Issues[i].Comments[j].Author != nil {
|
||||||
|
comment.Author = &jsonUser{
|
||||||
|
Login: mr.Issues[i].Comments[j].Author.Login,
|
||||||
|
RepoAssociation: getRepoAssociation(mr.Issues[i].Comments[j].Author),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue.Comments = append(issue.Comments, comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Results.RecentIssues = append(r.Results.RecentIssues, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.setDefaultCommitData(mr.DefaultBranchCommits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function shared between addMaintainedRawResults() and addCodeReviewRawResults().
|
||||||
|
func (r *jsonScorecardRawResult) setDefaultCommitData(commits []checker.DefaultBranchCommit) error {
|
||||||
|
if len(r.Results.DefaultBranchCommits) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
r.Results.DefaultBranchCommits = []jsonDefaultBranchCommit{}
|
r.Results.DefaultBranchCommits = []jsonDefaultBranchCommit{}
|
||||||
for _, commit := range cr.DefaultBranchCommits {
|
for _, commit := range commits {
|
||||||
com := jsonDefaultBranchCommit{
|
com := jsonDefaultBranchCommit{
|
||||||
Committer: jsonUser{
|
Committer: jsonUser{
|
||||||
Login: commit.Committer.Login,
|
Login: commit.Committer.Login,
|
||||||
|
// Note: repo association is not available. We could
|
||||||
|
// try to use issue information to set it, but we're likely to miss
|
||||||
|
// many anyway.
|
||||||
},
|
},
|
||||||
CommitMessage: commit.CommitMessage,
|
CommitMessage: commit.CommitMessage,
|
||||||
SHA: commit.SHA,
|
SHA: commit.SHA,
|
||||||
@ -158,6 +243,10 @@ func (r *jsonScorecardRawResult) addCodeReviewRawResults(cr *checker.CodeReviewD
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *jsonScorecardRawResult) addCodeReviewRawResults(cr *checker.CodeReviewData) error {
|
||||||
|
return r.setDefaultCommitData(cr.DefaultBranchCommits)
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:unparam
|
//nolint:unparam
|
||||||
func (r *jsonScorecardRawResult) addVulnerbilitiesRawResults(vd *checker.VulnerabilitiesData) error {
|
func (r *jsonScorecardRawResult) addVulnerbilitiesRawResults(vd *checker.VulnerabilitiesData) error {
|
||||||
r.Results.DatabaseVulnerabilities = []jsonDatabaseVulnerability{}
|
r.Results.DatabaseVulnerabilities = []jsonDatabaseVulnerability{}
|
||||||
@ -273,6 +362,11 @@ func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) err
|
|||||||
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maintained.
|
||||||
|
if err := r.addMaintainedRawResults(&raw.MaintainedResults); err != nil {
|
||||||
|
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ type RepoInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ScorecardResult struct is returned on a successful Scorecard run.
|
// ScorecardResult struct is returned on a successful Scorecard run.
|
||||||
|
//nolint
|
||||||
type ScorecardResult struct {
|
type ScorecardResult struct {
|
||||||
Repo RepoInfo
|
Repo RepoInfo
|
||||||
Date time.Time
|
Date time.Time
|
||||||
|
Loading…
Reference in New Issue
Block a user