Skip to content

Commit 5e67ab4

Browse files
authored
Merge branch 'main' into scorecards-cleanup
Signed-off-by: Arnaud J Le Hors <[email protected]>
2 parents 1417961 + 4692162 commit 5e67ab4

29 files changed

+324
-125
lines changed

.github/workflows/codeql-analysis.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161

6262
# Initializes the CodeQL tools for scanning.
6363
- name: Initialize CodeQL
64-
uses: github/codeql-action/init@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
64+
uses: github/codeql-action/init@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
6565
with:
6666
languages: ${{ matrix.language }}
6767
queries: +security-extended
@@ -73,7 +73,7 @@ jobs:
7373
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
7474
# If this step fails, then you should remove it and run the build manually (see below)
7575
- name: Autobuild
76-
uses: github/codeql-action/autobuild@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
76+
uses: github/codeql-action/autobuild@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
7777

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

8989
- name: Perform CodeQL Analysis
90-
uses: github/codeql-action/analyze@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
90+
uses: github/codeql-action/analyze@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1

.github/workflows/depsreview.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
- name: 'Checkout Repository'
2525
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
2626
- name: 'Dependency Review'
27-
uses: actions/dependency-review-action@30d582111533d59ab793fd9f971817241654f3ec
27+
uses: actions/dependency-review-action@11310527b429536e263dc6cc47873e608189ba21

.github/workflows/scorecard-analysis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ jobs:
4848
retention-days: 5
4949

5050
- name: "Upload SARIF results"
51-
uses: github/codeql-action/upload-sarif@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # v1
51+
uses: github/codeql-action/upload-sarif@678fc3afe258fb2e0cdc165ccf77b85719de7b3c # v1
5252
with:
5353
sarif_file: results.sarif

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ build-attestor: ## Runs go build on scorecard attestor
243243
# Run go build on scorecard attestor
244244
cd attestor/; CGO_ENABLED=0 go build -trimpath -a -tags netgo -ldflags '$(LDFLAGS)' -o scorecard-attestor
245245

246+
build-attestor-docker: ## Build scorecard-attestor Docker image
247+
build-attestor-docker:
248+
DOCKER_BUILDKIT=1 docker build . --file attestor/Dockerfile \
249+
--tag scorecard-attestor:latest \
250+
--tag scorecard-atttestor:$(GIT_HASH)
251+
246252
TOKEN_SERVER_DEPS = $(shell find clients/githubrepo/roundtripper/tokens/ -iname "*.go")
247253
build-github-server: ## Build GitHub token server
248254
build-github-server: clients/githubrepo/roundtripper/tokens/server/github-auth-server

attestor/Dockerfile

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2022 Security Scorecard Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
FROM golang@sha256:ea3d912d500b1ae0a691b2e53eb8a6345b579d42d7e6a64acca83d274b949740 AS base
16+
WORKDIR /src/scorecard
17+
COPY . ./
18+
19+
FROM base AS build
20+
ARG TARGETOS
21+
ARG TARGETARCH
22+
RUN make build-attestor
23+
24+
FROM gcr.io/google-appengine/debian11@sha256:fed7dd5b2c4bbfb70bd26a277cdaff98dced71f113632ccd5451dcc013fce0a4
25+
COPY --from=build /src/scorecard/attestor /
26+
ENTRYPOINT [ "/scorecard-attestor" ]
27+

attestor/README.md

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Scorecard Attestor
2+
3+
## What is scorecard-attestor?
4+
5+
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.
6+
7+
scorecard-attestor helps users secure their software deployment systems by ensuring the code that they deploy passes certain criteria.
8+
9+
## Building and using scorecard-attestor
10+
11+
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).
12+
13+
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.
14+
15+
## Configuring policies for scorecard-attestor
16+
17+
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).
18+
19+
### Policy schema
20+
21+
Policies follow the following schema:
22+
23+
```yaml
24+
---
25+
type: "//rec"
26+
optional:
27+
preventBinaryArtifacts: "//bool"
28+
allowedBinaryArtifacts:
29+
type: "//arr"
30+
contents: "//str" # Accepts glob-based filepaths as strings here
31+
ensureNoVulnerabilities: "//bool"
32+
ensureDependenciesPinned: "//bool"
33+
allowedUnpinnedDependencies:
34+
type: "//arr"
35+
contents:
36+
type: "//rec"
37+
optional:
38+
packagename: "//str"
39+
filepath: "//str"
40+
version: "//str"
41+
ensureCodeReviewed: "//bool"
42+
codeReviewRequirements:
43+
type: "//rec"
44+
optional:
45+
requiredApprovers:
46+
type: "//arr"
47+
contents: "//str"
48+
minReviewers: "//int"
49+
```
50+
51+
### Missing parameters
52+
53+
Policies that are left blank will be ignored. Policies that allow users additional configuration options will be given default parameters as listed below.
54+
55+
* `PreventBinaryArtifacts`: If not specified, `AllowedBinaryArtifacts` will be empty, i.e. no binary artifacts will be allowed
56+
* `PreventUnpinnedDependencies`: If not specified, `AllowedUnpinnedDependencies` will be empty, i.e. no unpinned dependencies will be allowed
57+
* `RequireCodeReviewed`: If not specified, `CodeReviewRequirements` will require at least one reviewer on all changesets.
58+
59+
## Sample
60+
61+
Examples of how to use scorecard-attestor with binary authorization in your project can be found in these two repos:
62+
63+
* [scorecard-binauthz-test-good](https://github.com/ossf-tests/scorecard-binauthz-test-good)
64+
* [scorecard-binauthz-test-bad](https://github.com/ossf-tests/scorecard-binauthz-test-bad)
65+
66+
Sample code comes with:
67+
68+
* `cloudbuild.yaml` to build the application and run scorecard-attestor
69+
* Terraform files to set up the binary authorization environment, including KMS and IAM.

attestor/command/check.go

+26-10
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,34 @@ import (
2626
"github.com/ossf/scorecard/v4/pkg"
2727
)
2828

29-
func runCheck() error {
29+
type EmptyParameterError struct {
30+
Param string
31+
}
32+
33+
func (ep EmptyParameterError) Error() string {
34+
return fmt.Sprintf("param %s is empty", ep.Param)
35+
}
36+
37+
func runCheck() (policy.PolicyResult, error) {
3038
ctx := context.Background()
3139
logger := sclog.NewLogger(sclog.DefaultLevel)
3240

3341
// Read the Binauthz attestation policy
3442
if policyPath == "" {
35-
return fmt.Errorf("policy path is empty")
43+
return policy.Fail, EmptyParameterError{Param: "policy"}
3644
}
45+
46+
var attestationPolicy *policy.AttestationPolicy
47+
3748
attestationPolicy, err := policy.ParseAttestationPolicyFromFile(policyPath)
3849
if err != nil {
39-
return fmt.Errorf("fail to load scorecard attestation policy: %v", err)
50+
return policy.Fail, fmt.Errorf("fail to load scorecard attestation policy: %w", err)
4051
}
4152

4253
if repoURL == "" {
4354
buildRepo := os.Getenv("REPO_NAME")
4455
if buildRepo == "" {
45-
return fmt.Errorf("repoURL not specified")
56+
return policy.Fail, EmptyParameterError{Param: "repoURL"}
4657
}
4758
repoURL = buildRepo
4859
logger.Info(fmt.Sprintf("Found repo URL %s Cloud Build environment", repoURL))
@@ -54,14 +65,18 @@ func runCheck() error {
5465
buildSHA := os.Getenv("COMMIT_SHA")
5566
if buildSHA == "" {
5667
logger.Info("commit not specified, running on HEAD")
68+
commitSHA = "HEAD"
5769
} else {
5870
commitSHA = buildSHA
59-
logger.Info(fmt.Sprintf("Found revision %s Cloud Build environment", commitSHA))
71+
logger.Info(fmt.Sprintf("Found revision %s from GCB build environment", commitSHA))
6072
}
6173
}
6274

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

6681
requiredChecks := attestationPolicy.GetRequiredChecksForPolicy()
6782

@@ -89,16 +104,17 @@ func runCheck() error {
89104
vulnsClient,
90105
)
91106
if err != nil {
92-
return fmt.Errorf("RunScorecard: %w", err)
107+
return policy.Fail, fmt.Errorf("RunScorecard: %w", err)
93108
}
94109

95110
result, err := attestationPolicy.EvaluateResults(&repoResult.RawResults)
96111
if err != nil {
97-
return fmt.Errorf("error when evaluating image %q against policy", image)
112+
return policy.Fail, fmt.Errorf("error when evaluating image %q against policy: %w", image, err)
98113
}
99114
if result != policy.Pass {
100-
return fmt.Errorf("image failed policy check %s:", image)
115+
logger.Info("image failed scorecard attestation policy check")
116+
} else {
117+
logger.Info("image passed scorecard attestation policy check")
101118
}
102-
logger.Info("Policy check passed")
103-
return nil
119+
return result, nil
104120
}

attestor/command/cli.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,31 @@ var RootCmd = &cobra.Command{
7474

7575
var checkAndSignCmd = &cobra.Command{
7676
Use: "attest",
77-
Short: "Run scorecard and sign a container image according to policy",
77+
Short: "Run scorecard and sign a container image if attestation policy check passes",
7878
RunE: func(cmd *cobra.Command, args []string) error {
79-
if err := runCheck(); err != nil {
79+
passed, err := runCheck()
80+
81+
if err != nil {
8082
return err
8183
}
82-
return runSign()
84+
85+
if passed {
86+
return runSign()
87+
}
88+
89+
return nil
8390
},
91+
SilenceUsage: true,
8492
}
8593

8694
var checkCmd = &cobra.Command{
8795
Use: "verify",
8896
Short: "Run scorecard and check an image against a policy",
8997
RunE: func(cmd *cobra.Command, args []string) error {
90-
return runCheck()
98+
_, err := runCheck()
99+
return err
91100
},
101+
SilenceUsage: true,
92102
}
93103

94104
func init() {

attestor/command/sign.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package command
1717
import (
1818
"fmt"
1919
"io/ioutil"
20+
"strings"
2021

2122
"github.com/grafeas/kritis/pkg/attestlib"
2223
"github.com/grafeas/kritis/pkg/kritis/metadata/containeranalysis"
@@ -47,6 +48,7 @@ func runSign() error {
4748
if kmsDigestAlg == "" {
4849
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")
4950
}
51+
kmsDigestAlg = strings.ToUpper(kmsDigestAlg)
5052
cSigner, err = signer.NewCloudKmsSigner(kmsKeyName, signer.DigestAlgorithm(kmsDigestAlg))
5153
if err != nil {
5254
return fmt.Errorf("creating kms signer failed: %v\n", err)
@@ -81,9 +83,9 @@ func runSign() error {
8183
// Parse attestation project
8284
if attestationProject == "" {
8385
attestationProject = util.GetProjectFromContainerImage(image)
84-
logger.Info(fmt.Sprintf("Using image project as attestation project: %s\n", attestationProject))
86+
logger.Info(fmt.Sprintf("Using image project as attestation project: %s", attestationProject))
8587
} else {
86-
logger.Info(fmt.Sprintf("Using specified attestation project: %s\n", attestationProject))
88+
logger.Info(fmt.Sprintf("Using specified attestation project: %s", attestationProject))
8789
}
8890

8991
// Check note name

attestor/e2e/command_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import (
1818
"strings"
1919
"testing"
2020

21-
"github.com/ossf/scorecard-attestor/command"
2221
"github.com/spf13/cobra"
22+
23+
"github.com/ossf/scorecard-attestor/command"
2324
)
2425

2526
func execute(t *testing.T, c *cobra.Command, args ...string) (string, error) {
@@ -35,6 +36,7 @@ func execute(t *testing.T, c *cobra.Command, args ...string) (string, error) {
3536
}
3637

3738
func TestRootCmd(t *testing.T) {
39+
t.Parallel()
3840
tt := []struct {
3941
name string
4042
args []string
@@ -51,7 +53,6 @@ func TestRootCmd(t *testing.T) {
5153

5254
for _, tc := range tt {
5355
_, err := execute(t, command.RootCmd, tc.args...)
54-
5556
if err != nil {
5657
t.Fatalf("%s: %s", tc.name, err)
5758
}

attestor/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ require (
2828
github.com/docker/docker-credential-helpers v0.7.0 // indirect
2929
github.com/fatih/color v1.13.0 // indirect
3030
github.com/gogo/protobuf v1.3.2 // indirect
31-
github.com/google/go-containerregistry v0.12.0 // indirect
31+
github.com/google/go-containerregistry v0.12.1 // indirect
3232
github.com/google/gofuzz v1.1.0 // indirect
3333
github.com/googleapis/gnostic v0.2.2 // indirect
3434
github.com/gophercloud/gophercloud v0.1.0 // indirect

attestor/go.sum

+3-3
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
427427
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
428428
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
429429
github.com/google/go-containerregistry v0.2.1/go.mod h1:Ts3Wioz1r5ayWx8sS6vLcWltWcM1aqFjd/eVrkFhrWM=
430-
github.com/google/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA=
431-
github.com/google/go-containerregistry v0.12.0/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k=
430+
github.com/google/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8=
431+
github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k=
432432
github.com/google/go-github/v38 v38.1.0 h1:C6h1FkaITcBFK7gAmq4eFzt6gbhEhk7L5z6R3Uva+po=
433433
github.com/google/go-github/v38 v38.1.0/go.mod h1:cStvrz/7nFr0FoENgG6GLbp53WaelXucT+BBz/3VKx4=
434434
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
@@ -707,7 +707,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
707707
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
708708
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
709709
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
710-
github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
710+
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
711711
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
712712
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
713713
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=

attestor/policy/attestation_policy.go

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ func (ap *AttestationPolicy) EvaluateResults(raw *checker.RawResults) (PolicyRes
9898
dl := checker.NewLogger()
9999
if ap.PreventBinaryArtifacts {
100100
checkResult, err := CheckPreventBinaryArtifacts(ap.AllowedBinaryArtifacts, raw, dl)
101-
102101
if !checkResult || err != nil {
103102
return checkResult, err
104103
}

0 commit comments

Comments
 (0)