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 60 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions 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: "Dockerfile"
image: "docker://gcr.io/openssf/scorecard-action:v2.0.0-beta.1"


199 changes: 199 additions & 0 deletions entrypoint/dependencydiff/comment_markdown.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// 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"
"math"
"net/url"
"os"
"sort"
"strconv"
"strings"

"github.com/google/go-github/v45/github"
"github.com/ossf/scorecard-action/options"
"github.com/ossf/scorecard/v4/checker"

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

const (
// negInif is "negative infinity" used for dependencydiff results ranking.
negInf float64 = -math.MaxFloat64
)

type scoreAndDependencyName struct {
dependencyName string
aggregateScore float64
}

func writeToComment(ctx context.Context, ghClient *github.Client, owner, repo string, report *string) error {
ref := os.Getenv(options.EnvGithubRef)
splitted := strings.Split(ref, "/")
// https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
// For a pull request-triggred workflow, the env GITHUB_REF has the following format:
// refs/pull/:prNumber/merge.
if len(splitted) != 4 {
return fmt.Errorf("%w: github ref", errEmpty)
}
prNumber, err := strconv.Atoi(splitted[2])
if err != nil {
return fmt.Errorf("error converting str pr number to int: %w", err)
}

// The current solution could result in a pull request full of our reports and drown out other comments.
// Create a new issue comment in the pull request and print the report there.

// A better solution is to check if there is an existing comment and update it if there is. However, the GitHub API
// only supports comment lookup by commentID, whose context will be lost if this runs again in the Action.
// GitHub API docs: https://docs.github.com/en/rest/issues/comments#get-an-issue-comment
// The go-github API: https://github.com/google/go-github/blob/master/github/issues_comments.go#L87

// TODO (#issue number): Try to update an existing comment first, create a new one iff. there is not.
_, _, err = ghClient.Issues.CreateComment(
ctx, owner, repo, prNumber,
&github.IssueComment{
Body: report,
},
)
if err != nil {
return fmt.Errorf("error creating comment: %w", err)
}
return nil
}

// dependencydiffResultsAsMarkdown exports the dependencydiff results as markdown.
func dependencydiffResultsAsMarkdown(depdiffResults []pkg.DependencyCheckResult,
base, head string) (*string, error) {

added, removed := dependencySliceToMaps(depdiffResults)
// Sort dependencies by their aggregate scores in descending orders.
addedSortKeys, err := getDependencySortKeys(added)
if err != nil {
return nil, err
}
removedSortKeys, err := getDependencySortKeys(removed)
if err != nil {
return nil, err
}
sort.SliceStable(
addedSortKeys,
func(i, j int) bool { return addedSortKeys[i].aggregateScore > addedSortKeys[j].aggregateScore },
)
sort.SliceStable(
removedSortKeys,
func(i, j int) bool { return removedSortKeys[i].aggregateScore > removedSortKeys[j].aggregateScore },
)
results := ""
for _, key := range addedSortKeys {
dName := key.dependencyName
if _, ok := added[dName]; !ok {
continue
}
current := addedTag()
if _, ok := removed[dName]; ok {
// Dependency in the added map also found in the removed map, indicating an updated one.
current += updatedTag()
}
newResult := added[dName]
if newResult.Ecosystem != nil && newResult.Version != nil {
ok, err := entryExists(*newResult.Ecosystem, newResult.Name, *newResult.Version)
if err != nil {
return nil, err
}
if ok {
current += depsDevTag(*newResult.Ecosystem, newResult.Name)
}
}
current += scoreTag(key.aggregateScore)

current += packageAsMarkdown(
newResult.Name, newResult.Version, newResult.SourceRepository, newResult.ChangeType,
)
if oldResult, ok := removed[dName]; ok {
current += packageAsMarkdown(
oldResult.Name, oldResult.Version, oldResult.SourceRepository, oldResult.ChangeType,
)
}
results += current + "\n\n"
}
for _, key := range removedSortKeys {
dName := key.dependencyName
if _, ok := added[dName]; ok {
// Skip updated ones.
continue
}
if _, ok := removed[dName]; !ok {
continue
}
current := removedTag()
if key.aggregateScore != checker.InconclusiveResultScore {
current += scoreTag(key.aggregateScore)
}
oldResult := removed[dName]
current += packageAsMarkdown(
oldResult.Name, oldResult.Version, oldResult.SourceRepository, oldResult.ChangeType,
)
results += current + "\n\n"
}
// TODO (#772):
out := "# [Scorecard Action](https://github.com/ossf/scorecard-action) Dependency-diff Report\n\n"
out += fmt.Sprintf(
"Dependency-diffs (changes) between the BASE reference `%s` and the HEAD reference `%s`:\n\n",
base, head,
)
if results == "" {
out += fmt.Sprintln("No dependency changes found.")
} else {
out += fmt.Sprintln(results)
}
out += experimentalFeature()
return &out, nil
}

func packageAsMarkdown(name string, version, srcRepo *string, changeType *pkg.ChangeType,
) string {
result := ""
result += fmt.Sprintf(" %s", name)
if srcRepo != nil {
result = "[" + result + "]" + "(" + *srcRepo + ")"
}
if version != nil {
result += fmt.Sprintf(" @ %s", *version)
}
if *changeType == pkg.Removed {
result = " ~~" + strings.Trim(result, " ") + "~~ "
}
return result
}

func experimentalFeature() string {
result := "> This is an experimental feature of the [Scorecard Action](https://github.com/ossf/scorecard-action). " +
"The [scores](https://github.com/ossf/scorecard#scoring) are aggregate scores calculated by the checks specified in the workflow file. " +
"Please refer to [Scorecard Checks](https://github.com/ossf/scorecard#scorecard-checks) for more details. " +
"Please also see the corresponding [deps.dev](https://deps.dev/) tag for a more comprehensive view of your dependencies."
return result
}

func depsDevTag(system, name string) string {
url := fmt.Sprintf(
"https://deps.dev/%s/%s",
url.PathEscape(strings.ToLower(system)),
url.PathEscape(strings.ToLower(name)),
)
return fmt.Sprintf(" **`[deps.dev](%s)`** ", url)
}