Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Feature: Dependency-diff Visualization in the Scorecard Action (version 0 - part 1) #780

Closed
wants to merge 74 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
1cb1447
save
aidenwang9867 Jul 25, 2022
15be8c4
save
aidenwang9867 Jul 25, 2022
12aecac
Merge branch 'depdiff_vis'
aidenwang9867 Jul 25, 2022
4b8b90e
Merge branch 'main' of https://github.com/aidenwang9867/scorecard-action
aidenwang9867 Jul 25, 2022
b1ad528
save'
aidenwang9867 Jul 25, 2022
f397d1d
save
aidenwang9867 Jul 25, 2022
41f75f6
save
aidenwang9867 Jul 25, 2022
60e5663
save
aidenwang9867 Jul 25, 2022
9051e6f
save
aidenwang9867 Jul 25, 2022
07593f8
Update action.yaml
aidenwang9867 Jul 26, 2022
d964f20
Update Dockerfile
aidenwang9867 Jul 26, 2022
69d3370
Update Dockerfile
aidenwang9867 Jul 26, 2022
f86644f
Update Dockerfile
aidenwang9867 Jul 26, 2022
8abd646
Update Makefile
aidenwang9867 Jul 26, 2022
6253a77
Update Makefile
aidenwang9867 Jul 26, 2022
3b7d225
Update Makefile
aidenwang9867 Jul 26, 2022
e46f58b
save
aidenwang9867 Jul 26, 2022
29ad97e
save
aidenwang9867 Jul 26, 2022
4d49883
save
aidenwang9867 Jul 26, 2022
f770d0b
save
aidenwang9867 Jul 27, 2022
756a5de
Merge branch 'ossf:main' into main
aidenwang9867 Jul 27, 2022
225f79d
save
aidenwang9867 Jul 27, 2022
d97f5ab
save
aidenwang9867 Jul 27, 2022
aaba2b1
save
aidenwang9867 Jul 27, 2022
547072f
save
aidenwang9867 Jul 27, 2022
21a09e0
save
aidenwang9867 Jul 27, 2022
4d7500d
save
aidenwang9867 Jul 27, 2022
add4807
save
aidenwang9867 Jul 27, 2022
1db21c7
save
aidenwang9867 Jul 27, 2022
e1eb7b0
save
aidenwang9867 Jul 27, 2022
6517cba
save
aidenwang9867 Jul 27, 2022
e270172
save
aidenwang9867 Jul 27, 2022
eb01349
save
aidenwang9867 Jul 27, 2022
769238c
save
aidenwang9867 Jul 27, 2022
d4c780c
save
aidenwang9867 Jul 27, 2022
dd2115e
save
aidenwang9867 Jul 27, 2022
9fa353d
save
aidenwang9867 Jul 27, 2022
ba01b03
save
aidenwang9867 Jul 27, 2022
421f46b
save
aidenwang9867 Jul 27, 2022
1f93c83
save
aidenwang9867 Jul 27, 2022
63778c9
save
aidenwang9867 Jul 28, 2022
b35c21d
save
aidenwang9867 Jul 28, 2022
688423a
save
aidenwang9867 Jul 28, 2022
6ee3a96
save
aidenwang9867 Jul 28, 2022
6059180
save
aidenwang9867 Jul 28, 2022
ed57136
save
aidenwang9867 Jul 28, 2022
6a10c80
save
aidenwang9867 Jul 28, 2022
dda85f2
save
aidenwang9867 Jul 28, 2022
57d7dd0
save
aidenwang9867 Jul 28, 2022
8798f49
save
aidenwang9867 Jul 28, 2022
07770d1
save
aidenwang9867 Jul 28, 2022
5c14fcd
Merge branch 'main' into depdiff_vis
aidenwang9867 Jul 28, 2022
eae4331
save
aidenwang9867 Jul 28, 2022
c3dd648
save
aidenwang9867 Jul 28, 2022
5b50a45
Merge branch 'main' into depdiff_vis
aidenwang9867 Jul 28, 2022
186206f
save
aidenwang9867 Jul 28, 2022
99427ba
Merge branch 'depdiff_vis' of https://github.com/aidenwang9867/scorec…
aidenwang9867 Jul 28, 2022
2a267e6
save
aidenwang9867 Jul 28, 2022
c29ebcd
save
aidenwang9867 Jul 28, 2022
852a3c9
added unit tests coverage
aidenwang9867 Jul 29, 2022
d59a2e9
save
aidenwang9867 Jul 29, 2022
65b75a4
save
aidenwang9867 Jul 30, 2022
9979eb5
save
aidenwang9867 Jul 30, 2022
60ddf06
Update action.yaml
aidenwang9867 Jul 30, 2022
4024ea8
save
aidenwang9867 Jul 30, 2022
b73e2fc
save
aidenwang9867 Jul 30, 2022
3499108
save
aidenwang9867 Jul 30, 2022
2bd9dbb
save
aidenwang9867 Jul 30, 2022
6460555
save
aidenwang9867 Jul 30, 2022
50f3d84
save
aidenwang9867 Jul 31, 2022
65706e4
save
aidenwang9867 Aug 1, 2022
7bc898c
save
aidenwang9867 Aug 2, 2022
e22d927
save
aidenwang9867 Aug 2, 2022
1e41d38
save
aidenwang9867 Aug 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,27 @@ inputs:
description: "INPUT: Default GitHub token. (Internal purpose only, not intended for developers to set. Used for pull requests configured with a PAT)."
required: false
default: ${{ github.token }}
checks:
description: "INPUT: Scorecard checks to run. Use this input to specify the checks to run for dependency-diffs."
required: false
default: "Maintained,Security-Policy,License,Code-Review,SAST" # Several important checks by default.
change_types:
description: "INPUT: Depenency-diff change types to surface Scorecard check results."
required: false
default: "added"
pull_request_head_sha:
description: "INPUT: The headSHA of the merging branch in a pull request. This is only used for a pull request-triggered action."
required: false
default: ${{ github.event.pull_request.head.sha }}


branding:
icon: "mic"
color: "white"

runs:
using: "docker"
image: "docker://gcr.io/openssf/scorecard-action:v2.0.0-beta.1"
image: "Dockerfile"
# image: "docker://gcr.io/openssf/scorecard-action:v2.0.0-beta.1"


82 changes: 82 additions & 0 deletions entrypoint/dependencydiff/entrypoint_depdiff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2022 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 dependencydiff

import (
"context"
"fmt"
"net/http"
"os"
"strings"

"github.com/google/go-github/v45/github"
"github.com/ossf/scorecard-action/options"
"github.com/ossf/scorecard/v4/clients/githubrepo/roundtripper"
"github.com/ossf/scorecard/v4/dependencydiff"
"github.com/ossf/scorecard/v4/log"
"github.com/ossf/scorecard/v4/pkg"
)

// New creates a new instance running the scorecard dependency-diff mode
// used as an entrypoint for GitHub Actions.
func New(ctx context.Context) error {
repoURI := os.Getenv(options.EnvGithubRepository)
ownerRepo := strings.Split(repoURI, "/")
if len(ownerRepo) != 2 {
return fmt.Errorf("%w: repo uri", errInvalid)
}
// Since the event listener is set to pull requests to main, this will be the main branch reference.
base := os.Getenv(options.EnvGithubBaseRef)
if base == "" {
return fmt.Errorf("%w: base ref", errEmpty)
}
// The head reference of the pull request source branch.
head := os.Getenv(options.EnvGitHubHeadRef)
if head == "" {
return fmt.Errorf("%w: head ref", errEmpty)
}
// GetDependencyDiffResults will handle the error checking of checks.
checks := strings.Split(os.Getenv(options.EnvInputChecks), ",")
changeTypes := strings.Split(os.Getenv(options.EnvInputChangeTypes), ",")
changeTypeMap := map[pkg.ChangeType]bool{}
for _, ct := range changeTypes {
key := pkg.ChangeType(ct)
if !key.IsValid() {
return fmt.Errorf("%w: change type", errInvalid)
}
changeTypeMap[key] = true
}
deps, err := dependencydiff.GetDependencyDiffResults(
ctx, repoURI, base, head, checks, changeTypeMap,
)
if err != nil {
return fmt.Errorf("error getting dependency-diff: %w", err)
}

// Generate a markdown string using the dependency-diffs and write it to the pull request comment.
report, err := dependencydiffResultsAsMarkdown(deps, base, head)
if err != nil {
return fmt.Errorf("error formatting results as markdown: %w", err)
}
logger := log.NewLogger(log.DefaultLevel)
ghrt := roundtripper.NewTransport(ctx, logger) /* This round tripper handles the access token. */
ghClient := github.NewClient(&http.Client{Transport: ghrt})
err = writeToComment(ctx, ghClient, ownerRepo[0], ownerRepo[1], report)
if err != nil {
return fmt.Errorf("error writting the report to comment: %w", err)
}

return nil
}
23 changes: 23 additions & 0 deletions entrypoint/dependencydiff/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2022 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 dependencydiff

import "errors"

var (
errEmpty = errors.New("empty")
errInvalid = errors.New("invalid")
errShouldNotBeNil = errors.New("should not be nil")
)
117 changes: 117 additions & 0 deletions entrypoint/dependencydiff/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2022 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 dependencydiff

import (
"fmt"
"net/http"
"net/url"

docs "github.com/ossf/scorecard/v4/docs/checks"
"github.com/ossf/scorecard/v4/pkg"
)

func getDependencySortKeys(dcMap map[string]pkg.DependencyCheckResult,
) ([]scoreAndDependencyName, error) {
sortKeys := []scoreAndDependencyName{}
doc, err := docs.Read()
if err != nil {
return nil, fmt.Errorf("error reading docs: %w", err)
}
for k := range dcMap {
scoreAndName := scoreAndDependencyName{
dependencyName: dcMap[k].Name,
aggregateScore: negInf,
// Since this struct is for sorting, the dependency having a score of negative infinite
// will be put to the very last, unless its agregate score is not empty.
}
scResults := dcMap[k].ScorecardResultWithError.ScorecardResult
if scResults != nil {
score, err := scResults.GetAggregateScore(doc)
if err != nil {
return nil, fmt.Errorf("error getting the aggregate score: %w", err)
}
scoreAndName.aggregateScore = score
}
sortKeys = append(sortKeys, scoreAndName)
}
return sortKeys, nil
}

func addedTag() string {
return fmt.Sprintf(" :sparkles: **`" + "added" + "`** ")
}

func removedTag() string {
return fmt.Sprintf(" ~~**`" + "removed" + "`**~~ ")
}

func scoreTag(score float64) string {
switch score {
case negInf:
return ""
default:
return fmt.Sprintf("`Score: %.1f` ", score)
}
}

// Convert the dependency-diff check result slice to two maps: added and removed, for added and removed dependencies respectively.
func dependencySliceToMaps(deps []pkg.DependencyCheckResult) (map[string]pkg.DependencyCheckResult,
map[string]pkg.DependencyCheckResult) {
added := map[string]pkg.DependencyCheckResult{}
removed := map[string]pkg.DependencyCheckResult{}
for _, d := range deps {
if d.ChangeType != nil {
switch *d.ChangeType {
case pkg.Added:
added[d.Name] = d
case pkg.Removed:
removed[d.Name] = d
case pkg.Updated:
// Do nothing, for now.
// The current data source GitHub Dependency Review won't give the updated dependencies,
// so we need to find them manually by checking the added/removed maps.
}
}
}
return added, removed
}

func entryExists(system, name, version string) (bool, error) {
url := fmt.Sprintf(
"https://deps.dev/_/s/%s/p/%s/v/%s",
url.PathEscape(system),
url.PathEscape(name),
url.PathEscape(version),
)
resp, err := http.Get(url)
if err != nil {
return false, fmt.Errorf("error requesting deps.dec/_: %w", err)
}
switch resp.StatusCode {
case http.StatusOK:
return true, nil
default:
return false, nil
}
}

func asPointerChangeType(ct pkg.ChangeType) *pkg.ChangeType {
return &ct
}

func asPointerStr(s string) *string {
return &s
}
Loading