Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentsimon committed May 17, 2022
1 parent 6406cfd commit 66135c9
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 48 deletions.
25 changes: 24 additions & 1 deletion checker/raw_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type RawResults struct {
BranchProtectionResults BranchProtectionsData
CodeReviewResults CodeReviewData
WebhookResults WebhooksData
ContributorsResults ContributorsData
MaintainedResults MaintainedData
SignedReleasesResults SignedReleasesData
LicenseResults LicenseData
Expand All @@ -54,6 +55,11 @@ type CodeReviewData struct {
DefaultBranchCommits []DefaultBranchCommit
}

// ContributorsData represents contributor information.
type ContributorsData struct {
Users []User
}

// VulnerabilitiesData contains the raw results
// for the Vulnerabilities check.
type VulnerabilitiesData struct {
Expand Down Expand Up @@ -194,15 +200,32 @@ type MergeRequest struct {

// Review represent a review using the built-in review system.
type Review struct {
Reviewer User
State string
Reviewer User
// TODO(Review): add fields here if needed.
}

// User represent a user.
type User struct {
RepoAssociation *RepoAssociation
Login string
// Orgnization refers to a GitHub org.
Organizations []Organization
// Companies refer to a claim by a user in their profile.
Companies []Company
NumContributions uint
}

// Organization represents a GitHub organization.
type Organization struct {
Login string
// TODO: other info.
}

// Company represents a company in a user's profile.
type Company struct {
Name string
// TODO: other info.
}

// RepoAssociation represents a user relationship with a repo.
Expand Down
54 changes: 11 additions & 43 deletions checks/contributors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,14 @@
package checks

import (
"fmt"
"strings"

"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/evaluation"
"github.com/ossf/scorecard/v4/checks/raw"
sce "github.com/ossf/scorecard/v4/errors"
)

const (
minContributionsPerUser = 5
numberCompaniesForTopScore = 3
// CheckContributors is the registered name for Contributors.
CheckContributors = "Contributors"
)
// CheckContributors is the registered name for Contributors.
const CheckContributors = "Contributors"

//nolint:gochecknoinits
func init() {
Expand All @@ -39,44 +34,17 @@ func init() {

// Contributors run Contributors check.
func Contributors(c *checker.CheckRequest) checker.CheckResult {
contribs, err := c.RepoClient.ListContributors()
rawData, err := raw.Contributors(c.RepoClient)
if err != nil {
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("Client.Repositories.ListContributors: %v", err))
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckContributors, e)
}

companies := map[string]struct{}{}
for _, contrib := range contribs {
if contrib.NumContributions < minContributionsPerUser {
continue
}

for _, org := range contrib.Organizations {
if org.Login != "" {
companies[org.Login] = struct{}{}
}
}

company := contrib.Company
if company != "" {
company = strings.ToLower(company)
company = strings.ReplaceAll(company, "inc.", "")
company = strings.ReplaceAll(company, "llc", "")
company = strings.ReplaceAll(company, ",", "")
company = strings.TrimLeft(company, "@")
company = strings.Trim(company, " ")
companies[company] = struct{}{}
}
}
names := []string{}
for c := range companies {
names = append(names, c)
// Return raw results.
if c.RawResults != nil {
c.RawResults.ContributorsResults = rawData
}

c.Dlogger.Info(&checker.LogMessage{
Text: fmt.Sprintf("contributors work for: %v", strings.Join(names, ",")),
})

reason := fmt.Sprintf("%d different companies found", len(companies))
return checker.CreateProportionalScoreResult(CheckContributors, reason, len(companies), numberCompaniesForTopScore)
// Return the score evaluation.
return evaluation.Contributors(CheckContributors, c.Dlogger, &rawData)
}
75 changes: 75 additions & 0 deletions checks/evaluation/contributors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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"
"sort"
"strings"

"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
)

const (
minContributionsPerUser = 5
numberCompaniesForTopScore = 3
)

// Contributors applies the score policy for the Contributors check.
func Contributors(name string, dl checker.DetailLogger,
r *checker.ContributorsData,
) checker.CheckResult {
if r == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
return checker.CreateRuntimeErrorResult(name, e)
}

entities := make(map[string]bool)

for _, user := range r.Users {
if user.NumContributions < minContributionsPerUser {
continue
}

for _, org := range user.Organizations {
entities[org.Login] = true
}

for _, comp := range user.Companies {
entities[comp.Name] = true
}
}

names := []string{}
for c := range entities {
names = append(names, c)
}

sort.Strings(names)

if len(name) > 0 {
dl.Info(&checker.LogMessage{
Text: fmt.Sprintf("contributors work for %v", strings.Join(names, ",")),
})
} else {
dl.Warn(&checker.LogMessage{
Text: "no contributors have an org or company",
})
}

reason := fmt.Sprintf("%d different organizations found", len(entities))
return checker.CreateProportionalScoreResult(name, reason, len(entities), numberCompaniesForTopScore)
}
89 changes: 89 additions & 0 deletions checks/raw/contributors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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 raw

import (
"fmt"
"strings"

"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/clients"
)

// Contributors retrieves the raw data for the Contributors check.
func Contributors(c clients.RepoClient) (checker.ContributorsData, error) {
var users []checker.User

contribs, err := c.ListContributors()
if err != nil {
return checker.ContributorsData{}, fmt.Errorf("Client.Repositories.ListContributors: %w", err)
}

for _, contrib := range contribs {
user := checker.User{
Login: contrib.User.Login,
NumContributions: uint(contrib.NumContributions),
}

for _, org := range contrib.Organizations {
if org.Login != "" && !orgContains(user.Organizations, org.Login) {
user.Organizations = append(user.Organizations,
checker.Organization{
Login: org.Login,
},
)
}
}

company := contrib.Company
if company != "" {
company = strings.ToLower(company)
company = strings.ReplaceAll(company, "inc.", "")
company = strings.ReplaceAll(company, "llc", "")
company = strings.ReplaceAll(company, ",", "")
company = strings.TrimLeft(company, "@")
company = strings.Trim(company, " ")
if company != "" && !companyContains(user.Companies, company) {
user.Companies = append(user.Companies,
checker.Company{
Name: company,
},
)
}
}

users = append(users, user)
}

return checker.ContributorsData{Users: users}, nil
}

func companyContains(cs []checker.Company, name string) bool {
for _, a := range cs {
if a.Name == name {
return true
}
}
return false
}

func orgContains(os []checker.Organization, login string) bool {
for _, a := range os {
if a.Login == login {
return true
}
}
return false
}
Loading

0 comments on commit 66135c9

Please sign in to comment.