Skip to content

Commit

Permalink
initial implementation of ossf#1369 (comment) to provide more license…
Browse files Browse the repository at this point in the history
… details

Signed-off-by: Scott Hissam <[email protected]>
  • Loading branch information
shissam committed Nov 7, 2022
1 parent c46a581 commit edcc8cf
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 10 deletions.
27 changes: 26 additions & 1 deletion checker/raw_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,35 @@ type MaintainedData struct {
ArchivedStatus ArchivedStatus
}

type LicenseAttributionType string

const (
// forms of security policy hints being evaluated.
LicenseAttributionTypeOther LicenseAttributionType = "otherAttribution"
LicenseAttributionTypeRepo LicenseAttributionType = "repoAttribution"
LicenseAttributionTypeScorecard LicenseAttributionType = "scorecardAttribution"
)

// license details
type License struct {
Key string // repo specified key
Name string // OSI standardized license name
Size int // size of the license file found (default: 0)
SpdxId string // SPDX standardized identifier
Attribution LicenseAttributionType // source of licensing information
}

// one file contains one license
type LicenseFile struct {
File File
License License
}

// LicenseData contains the raw results
// for the License check.
// Some repos may have more than one license.
type LicenseData struct {
Files []File
LicenseFiles []LicenseFile
}

// CodeReviewData contains the raw results
Expand Down
6 changes: 3 additions & 3 deletions checks/evaluation/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ func License(name string, dl checker.DetailLogger,
}

// Apply the policy evaluation.
if r.Files == nil || len(r.Files) == 0 {
if r.LicenseFiles == nil || len(r.LicenseFiles) == 0 {
return checker.CreateMinScoreResult(name, "license file not detected")
}

for _, f := range r.Files {
for _, f := range r.LicenseFiles {
dl.Info(&checker.LogMessage{
Path: f.Path,
Path: f.File.Path,
Type: checker.FileTypeSource,
Offset: 1,
})
Expand Down
41 changes: 37 additions & 4 deletions checks/raw/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,49 @@ func License(c *checker.CheckRequest) (checker.LicenseData, error) {
var results checker.LicenseData
var path string

licensesFound, lerr := c.RepoClient.ListLicenses()
if lerr == nil && len(licensesFound) > 0 {
//fmt.Printf ("'%T' of size '%d' has '%+v'\n", licensesFound, len(licensesFound), licensesFound)
for _, v := range licensesFound {
results.LicenseFiles = append(results.LicenseFiles,
checker.LicenseFile{
File: checker.File{
Path: v.Path,
Type: checker.FileTypeSource,
},
License: checker.License{
Key: v.Key,
Name: v.Name,
Size: v.Size,
SpdxId: v.SPDXId,
Attribution: checker.LicenseAttributionTypeRepo,
},
})
}
return results, nil
}

// no licenses reported by repo API, continue looking for files
err := fileparser.OnAllFilesDo(c.RepoClient, isLicenseFile, &path)
if err != nil {
return results, fmt.Errorf("fileparser.OnAllFilesDo: %w", err)
}

// scorecard search stops at first candidate (isLicenseFile) license file found
if path != "" {
results.Files = append(results.Files,
checker.File{
Path: path,
Type: checker.FileTypeSource,
results.LicenseFiles = append(results.LicenseFiles,
checker.LicenseFile{
File: checker.File{
Path: path,
Type: checker.FileTypeSource,
},
License: checker.License{
Key: "",
Name: "",
Size: int(0),
SpdxId: "",
Attribution: checker.LicenseAttributionTypeScorecard,
},
})
}

Expand Down
12 changes: 12 additions & 0 deletions clients/githubrepo/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type Client struct {
searchCommits *searchCommitsHandler
webhook *webhookHandler
languages *languagesHandler
licenses *licensesHandler
ctx context.Context
tarball tarballHandler
}
Expand Down Expand Up @@ -113,6 +114,9 @@ func (client *Client) InitRepo(inputRepo clients.Repo, commitSHA string) error {

// Setup languagesHandler.
client.languages.init(client.ctx, client.repourl)

// Setup licensesHandler.
client.licenses.init(client.ctx, client.repourl)
return nil
}

Expand Down Expand Up @@ -213,6 +217,11 @@ func (client *Client) ListProgrammingLanguages() ([]clients.Language, error) {
return client.languages.listProgrammingLanguages()
}

// ListLicenses implements RepoClient.ListLicenses
func (client *Client) ListLicenses() ([]clients.License, error) {
return client.licenses.listLicenses()
}

// Search implements RepoClient.Search.
func (client *Client) Search(request clients.SearchRequest) (clients.SearchResponse, error) {
return client.search.search(request)
Expand Down Expand Up @@ -273,6 +282,9 @@ func CreateGithubRepoClientWithTransport(ctx context.Context, rt http.RoundTripp
languages: &languagesHandler{
ghclient: client,
},
licenses: &licensesHandler{
ghclient: client,
},
tarball: tarballHandler{
httpClient: httpClient,
},
Expand Down
90 changes: 90 additions & 0 deletions clients/githubrepo/licenses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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 githubrepo

import (
"context"
"fmt"
"path"
"sync"

"github.com/google/go-github/v38/github"

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

type licensesHandler struct {
ghclient *github.Client
once *sync.Once
ctx context.Context
errSetup error
repourl *repoURL
licenses []clients.License
}

func (handler *licensesHandler) init(ctx context.Context, repourl *repoURL) {
handler.ctx = ctx
handler.repourl = repourl
handler.errSetup = nil
handler.once = new(sync.Once)
fmt.Println ("licenses handler initialized\n")
}

// TODO: Can add support to parse the raw response JSON and mark licenses that are not in
// our defined License consts in clients/licenses.go as "not supported licenses".
func (handler *licensesHandler) setup() error {
handler.once.Do(func() {
client := handler.ghclient
// defined at docs.github.com/en/rest/licenses#get-the-license-for-a-repository
reqURL := path.Join("repos", handler.repourl.owner, handler.repourl.repo, "license")
req, err := client.NewRequest("GET", reqURL, nil)
if err != nil {
handler.errSetup = fmt.Errorf("request for repo license failed with %w", err)
return
}
bodyJSON := github.RepositoryLicense{}
// The client.repoClient.Do API writes the response body to var bodyJSON,
// so we can ignore the first returned variable (the entire http response object)
// since we only need the response body here.
_, err = client.Do(handler.ctx, req, &bodyJSON)
if err != nil {
handler.errSetup = fmt.Errorf("response for repo license failed with %w", err)
return
}

// TODO: github.RepositoryLicense{} only supports one license per repo
// should that change to an array of licenses, the change would
// be here to iterate over any such range.
handler.licenses = append(handler.licenses, clients.License{
Key: bodyJSON.GetLicense().GetKey(),
Name: bodyJSON.GetLicense().GetName(),
SPDXId: bodyJSON.GetLicense().GetSPDXID(),
Path: bodyJSON.GetName(),
Type: bodyJSON.GetType(),
Size: bodyJSON.GetSize(),
},
)
handler.errSetup = nil
})

return handler.errSetup
}

func (handler *licensesHandler) listLicenses() ([]clients.License, error) {
if err := handler.setup(); err != nil {
return nil, fmt.Errorf("error during licensesHandler.setup: %w", err)
}
return handler.licenses, nil
}
26 changes: 26 additions & 0 deletions clients/licenses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 clients

// License represents a customized struct for licenses used by clients.
// from pkg.go.dev/github.com/google/go-github/github#RepositoryLicense
type License struct {
Key string // RepositoryLicense.GetLicense().GetKey()
Name string // RepositoryLicense.GetLicense().GetName()
Path string // RepositoryLicense.GetName()
Size int // RepositoryLicense.GetSize()
SPDXId string // RepositoryLicense.GetLicense().GetSPDXID()
Type string // RepositoryLicense.GetType()
}
6 changes: 6 additions & 0 deletions clients/localdir/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ func (client *localDirClient) ListProgrammingLanguages() ([]clients.Language, er
return nil, fmt.Errorf("ListProgrammingLanguages: %w", clients.ErrUnsupportedFeature)
}

// ListLicenses implements RepoClient.ListLicenses.
// TODO: add ListLicenses support for local directories.
func (client *localDirClient) ListLicenses() ([]clients.License, error) {
return nil, fmt.Errorf("ListLicenses: %w", clients.ErrUnsupportedFeature)
}

func (client *localDirClient) GetCreatedAt() (time.Time, error) {
return time.Time{}, fmt.Errorf("GetCreatedAt: %w", clients.ErrUnsupportedFeature)
}
Expand Down
1 change: 1 addition & 0 deletions clients/repo_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type RepoClient interface {
GetDefaultBranch() (*BranchRef, error)
ListCommits() ([]Commit, error)
ListIssues() ([]Issue, error)
ListLicenses() ([]License, error)
ListReleases() ([]Release, error)
ListContributors() ([]User, error)
ListSuccessfulWorkflowRuns(filename string) ([]WorkflowRun, error)
Expand Down
4 changes: 2 additions & 2 deletions pkg/json_raw_results.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,11 +563,11 @@ func (r *jsonScorecardRawResult) addCodeReviewRawResults(cr *checker.CodeReviewD
//nolint:unparam
func (r *jsonScorecardRawResult) addLicenseRawResults(ld *checker.LicenseData) error {
r.Results.Licenses = []jsonLicense{}
for _, file := range ld.Files {
for _, file := range ld.LicenseFiles {
r.Results.Licenses = append(r.Results.Licenses,
jsonLicense{
File: jsonFile{
Path: file.Path,
Path: file.File.Path,
},
},
)
Expand Down

0 comments on commit edcc8cf

Please sign in to comment.