Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/ossf/scorecard into main
Browse files Browse the repository at this point in the history
  • Loading branch information
shissam committed Nov 26, 2022
2 parents 37cf58e + d8fefc9 commit 541d38f
Show file tree
Hide file tree
Showing 55 changed files with 507 additions and 174 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1

uses: github/codeql-action/init@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
with:
languages: ${{ matrix.language }}
queries: +security-extended
Expand All @@ -73,7 +74,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
uses: github/codeql-action/autobuild@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand All @@ -87,4 +88,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
uses: github/codeql-action/analyze@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
2 changes: 1 addition & 1 deletion .github/workflows/depsreview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: 'Dependency Review'
uses: actions/dependency-review-action@30d582111533d59ab793fd9f971817241654f3ec
uses: actions/dependency-review-action@11310527b429536e263dc6cc47873e608189ba21
1 change: 1 addition & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
uses: nick-invision/retry@3e91a01664abd3c5cd539100d10d33b9c5b68482
env:
GITHUB_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLAB_AUTH_TOKEN: ${{ secrets.GITLAB_TOKEN }}
with:
max_attempts: 3
retry_on: error
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/scorecard-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ on:
schedule:
# Weekly on Saturdays.
- cron: '30 1 * * 6'
#pull_request:
# All branches are supported.
# branches: [main]
pull_request:
branches: [main]

permissions: read-all

Expand Down Expand Up @@ -48,6 +47,6 @@ jobs:
retention-days: 5

- name: "Upload SARIF results"
uses: github/codeql-action/upload-sarif@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
uses: github/codeql-action/upload-sarif@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
with:
sarif_file: results.sarif
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ build-attestor: ## Runs go build on scorecard attestor
# Run go build on scorecard attestor
cd attestor/; CGO_ENABLED=0 go build -trimpath -a -tags netgo -ldflags '$(LDFLAGS)' -o scorecard-attestor


build-attestor-docker: ## Build scorecard-attestor Docker image
build-attestor-docker:
DOCKER_BUILDKIT=1 docker build . --file attestor/Dockerfile \
--tag scorecard-attestor:latest \
--tag scorecard-atttestor:$(GIT_HASH)

TOKEN_SERVER_DEPS = $(shell find clients/githubrepo/roundtripper/tokens/ -iname "*.go")
build-github-server: ## Build GitHub token server
build-github-server: clients/githubrepo/roundtripper/tokens/server/github-auth-server
Expand Down
27 changes: 27 additions & 0 deletions attestor/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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.

FROM golang@sha256:ea3d912d500b1ae0a691b2e53eb8a6345b579d42d7e6a64acca83d274b949740 AS base
WORKDIR /src/scorecard
COPY . ./

FROM base AS build
ARG TARGETOS
ARG TARGETARCH
RUN make build-attestor

FROM gcr.io/google-appengine/debian11@sha256:fed7dd5b2c4bbfb70bd26a277cdaff98dced71f113632ccd5451dcc013fce0a4
COPY --from=build /src/scorecard/attestor /
ENTRYPOINT [ "/scorecard-attestor" ]

69 changes: 69 additions & 0 deletions attestor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Scorecard Attestor

## What is scorecard-attestor?

scorecard-attestor is a tool that runs scorecard on a software source repo, and based on certain policies about those results, produces a Google Cloud binary authorization attestation.

scorecard-attestor helps users secure their software deployment systems by ensuring the code that they deploy passes certain criteria.

## Building and using scorecard-attestor

scorecard-attestor can be built as a standalone binary from source using `make build-attestor`, or with Docker, using `make build-attestor-docker`. scorecard-attestor is intended to be used as part of a Google Cloud Build pipeline, and inherits environment variables based on [build substitutions](https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values).

Unless there's an internal error, scorecard-attestor will always return a successful status code, but will only produce a binary authorization attestation if the policy check passes.

## Configuring policies for scorecard-attestor

Policies for scorecard attestor can be passed through the CLI using the `--policy` flag. Examples of policies can be seen in [attestor/policy/testdata](/attestor/policy/testdata).

### Policy schema

Policies follow the following schema:

```yaml
---
type: "//rec"
optional:
preventBinaryArtifacts: "//bool"
allowedBinaryArtifacts:
type: "//arr"
contents: "//str" # Accepts glob-based filepaths as strings here
ensureNoVulnerabilities: "//bool"
ensureDependenciesPinned: "//bool"
allowedUnpinnedDependencies:
type: "//arr"
contents:
type: "//rec"
optional:
packagename: "//str"
filepath: "//str"
version: "//str"
ensureCodeReviewed: "//bool"
codeReviewRequirements:
type: "//rec"
optional:
requiredApprovers:
type: "//arr"
contents: "//str"
minReviewers: "//int"
```
### Missing parameters
Policies that are left blank will be ignored. Policies that allow users additional configuration options will be given default parameters as listed below.
* `PreventBinaryArtifacts`: If not specified, `AllowedBinaryArtifacts` will be empty, i.e. no binary artifacts will be allowed
* `PreventUnpinnedDependencies`: If not specified, `AllowedUnpinnedDependencies` will be empty, i.e. no unpinned dependencies will be allowed
* `RequireCodeReviewed`: If not specified, `CodeReviewRequirements` will require at least one reviewer on all changesets.

## Sample

Examples of how to use scorecard-attestor with binary authorization in your project can be found in these two repos:

* [scorecard-binauthz-test-good](https://github.com/ossf-tests/scorecard-binauthz-test-good)
* [scorecard-binauthz-test-bad](https://github.com/ossf-tests/scorecard-binauthz-test-bad)

Sample code comes with:

* `cloudbuild.yaml` to build the application and run scorecard-attestor
* Terraform files to set up the binary authorization environment, including KMS and IAM.
37 changes: 27 additions & 10 deletions attestor/command/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,34 @@ import (
"github.com/ossf/scorecard/v4/pkg"
)

func runCheck() error {
type EmptyParameterError struct {
Param string
}

func (ep EmptyParameterError) Error() string {
return fmt.Sprintf("param %s is empty", ep.Param)
}

func runCheck() (policy.PolicyResult, error) {
ctx := context.Background()
logger := sclog.NewLogger(sclog.DefaultLevel)

// Read the Binauthz attestation policy
if policyPath == "" {
return fmt.Errorf("policy path is empty")
return policy.Fail, EmptyParameterError{Param: "policy"}
}

var attestationPolicy *policy.AttestationPolicy

attestationPolicy, err := policy.ParseAttestationPolicyFromFile(policyPath)
if err != nil {
return fmt.Errorf("fail to load scorecard attestation policy: %v", err)
return policy.Fail, fmt.Errorf("fail to load scorecard attestation policy: %w", err)
}

if repoURL == "" {
buildRepo := os.Getenv("REPO_NAME")
if buildRepo == "" {
return fmt.Errorf("repoURL not specified")
return policy.Fail, EmptyParameterError{Param: "repoURL"}
}
repoURL = buildRepo
logger.Info(fmt.Sprintf("Found repo URL %s Cloud Build environment", repoURL))
Expand All @@ -54,14 +65,18 @@ func runCheck() error {
buildSHA := os.Getenv("COMMIT_SHA")
if buildSHA == "" {
logger.Info("commit not specified, running on HEAD")
commitSHA = "HEAD"
} else {
commitSHA = buildSHA
logger.Info(fmt.Sprintf("Found revision %s Cloud Build environment", commitSHA))
logger.Info(fmt.Sprintf("Found revision %s from GCB build environment", commitSHA))
}
}

repo, repoClient, ossFuzzRepoClient, ciiClient, vulnsClient, err := checker.GetClients(
ctx, repoURL, "", logger)
if err != nil {
return policy.Fail, fmt.Errorf("couldn't set up clients: %w", err)
}

requiredChecks := attestationPolicy.GetRequiredChecksForPolicy()

Expand All @@ -82,23 +97,25 @@ func runCheck() error {
ctx,
repo,
commitSHA,
0,
enabledChecks,
repoClient,
ossFuzzRepoClient,
ciiClient,
vulnsClient,
)
if err != nil {
return fmt.Errorf("RunScorecards: %w", err)
return policy.Fail, fmt.Errorf("RunScorecards: %w", err)
}

result, err := attestationPolicy.EvaluateResults(&repoResult.RawResults)
if err != nil {
return fmt.Errorf("error when evaluating image %q against policy", image)
return policy.Fail, fmt.Errorf("error when evaluating image %q against policy: %w", image, err)
}
if result != policy.Pass {
return fmt.Errorf("image failed policy check %s:", image)
logger.Info("image failed scorecard attestation policy check")
} else {
logger.Info("image passed scorecard attestation policy check")
}
logger.Info("Policy check passed")
return nil
return result, nil
}
18 changes: 14 additions & 4 deletions attestor/command/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,31 @@ var RootCmd = &cobra.Command{

var checkAndSignCmd = &cobra.Command{
Use: "attest",
Short: "Run scorecard and sign a container image according to policy",
Short: "Run scorecard and sign a container image if attestation policy check passes",
RunE: func(cmd *cobra.Command, args []string) error {
if err := runCheck(); err != nil {
passed, err := runCheck()

if err != nil {
return err
}
return runSign()

if passed {
return runSign()
}

return nil
},
SilenceUsage: true,
}

var checkCmd = &cobra.Command{
Use: "verify",
Short: "Run scorecard and check an image against a policy",
RunE: func(cmd *cobra.Command, args []string) error {
return runCheck()
_, err := runCheck()
return err
},
SilenceUsage: true,
}

func init() {
Expand Down
6 changes: 4 additions & 2 deletions attestor/command/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package command
import (
"fmt"
"io/ioutil"
"strings"

"github.com/grafeas/kritis/pkg/attestlib"
"github.com/grafeas/kritis/pkg/kritis/metadata/containeranalysis"
Expand Down Expand Up @@ -47,6 +48,7 @@ func runSign() error {
if kmsDigestAlg == "" {
return fmt.Errorf("kms_digest_alg is unspecified, must be one of SHA256|SHA384|SHA512, and the same as specified by the key version's algorithm")
}
kmsDigestAlg = strings.ToUpper(kmsDigestAlg)
cSigner, err = signer.NewCloudKmsSigner(kmsKeyName, signer.DigestAlgorithm(kmsDigestAlg))
if err != nil {
return fmt.Errorf("creating kms signer failed: %v\n", err)
Expand Down Expand Up @@ -81,9 +83,9 @@ func runSign() error {
// Parse attestation project
if attestationProject == "" {
attestationProject = util.GetProjectFromContainerImage(image)
logger.Info(fmt.Sprintf("Using image project as attestation project: %s\n", attestationProject))
logger.Info(fmt.Sprintf("Using image project as attestation project: %s", attestationProject))
} else {
logger.Info(fmt.Sprintf("Using specified attestation project: %s\n", attestationProject))
logger.Info(fmt.Sprintf("Using specified attestation project: %s", attestationProject))
}

// Check note name
Expand Down
6 changes: 4 additions & 2 deletions attestor/e2e/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (
"strings"
"testing"

"github.com/ossf/scorecard-attestor/command"

"github.com/spf13/cobra"

"github.com/ossf/scorecard-attestor/command"
)

func execute(t *testing.T, c *cobra.Command, args ...string) (string, error) {
Expand All @@ -35,6 +37,7 @@ func execute(t *testing.T, c *cobra.Command, args ...string) (string, error) {
}

func TestRootCmd(t *testing.T) {
t.Parallel()
tt := []struct {
name string
args []string
Expand All @@ -51,7 +54,6 @@ func TestRootCmd(t *testing.T) {

for _, tc := range tt {
_, err := execute(t, command.RootCmd, tc.args...)

if err != nil {
t.Fatalf("%s: %s", tc.name, err)
}
Expand Down
2 changes: 1 addition & 1 deletion attestor/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
Expand Down
1 change: 0 additions & 1 deletion attestor/policy/attestation_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ func (ap *AttestationPolicy) EvaluateResults(raw *checker.RawResults) (PolicyRes
dl := checker.NewLogger()
if ap.PreventBinaryArtifacts {
checkResult, err := CheckPreventBinaryArtifacts(ap.AllowedBinaryArtifacts, raw, dl)

if !checkResult || err != nil {
return checkResult, err
}
Expand Down
2 changes: 1 addition & 1 deletion checks/binary_artifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestBinaryArtifacts(t *testing.T) {
ctx := context.Background()

client := localdir.CreateLocalDirClient(ctx, logger)
if err := client.InitRepo(repo, clients.HeadSHA); err != nil {
if err := client.InitRepo(repo, clients.HeadSHA, 0); err != nil {
t.Errorf("InitRepo: %v", err)
}

Expand Down
2 changes: 1 addition & 1 deletion checks/license_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestLicenseFileSubdirectory(t *testing.T) {
ctx := context.Background()

client := localdir.CreateLocalDirClient(ctx, logger)
if err := client.InitRepo(repo, clients.HeadSHA); err != nil {
if err := client.InitRepo(repo, clients.HeadSHA, 0); err != nil {
t.Errorf("InitRepo: %v", err)
}

Expand Down
3 changes: 2 additions & 1 deletion checks/raw/security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ func SecurityPolicy(c *checker.CheckRequest) (checker.SecurityPolicyData, error)
// https#://docs.github.com/en/github/building-a-strong-community/creating-a-default-community-health-file.
// TODO(1491): Make this non-GitHub specific.
logger := log.NewLogger(log.InfoLevel)
// HAD TO HARD CODE TO 30
dotGitHubClient := githubrepo.CreateGithubRepoClient(c.Ctx, logger)
err = dotGitHubClient.InitRepo(c.Repo.Org(), clients.HeadSHA)
err = dotGitHubClient.InitRepo(c.Repo.Org(), clients.HeadSHA, 0)
switch {
case err == nil:
defer dotGitHubClient.Close()
Expand Down
Loading

0 comments on commit 541d38f

Please sign in to comment.