Skip to content

Commit

Permalink
Merge branch 'main' into fix/branch-protection-score
Browse files Browse the repository at this point in the history
  • Loading branch information
spencerschrock authored Jul 18, 2023
2 parents 1f23318 + ac6e027 commit 3a4ad09
Show file tree
Hide file tree
Showing 23 changed files with 373 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
fetch-depth: 2 # needed to diff changed files
- id: files
name: Get changed files
uses: tj-actions/changed-files@54849deb963ca9f24185fb5de2965e002d066e6b #v37.0.5
uses: tj-actions/changed-files@2a968ff601949c81b47d9c1fdb789b0d25ddeea2 #v37.1.2
with:
files_ignore: '**.md'
- id: docs_only_check
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
command: make e2e-pat
- name: codecov
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # 2.1.0
if: ${{ github.event_name != 'pull_request' }}
if: ${{ github.event_name != 'pull_request' || github.actor != 'dependabot[bot]' }}
with:
files: "*e2e-coverage.out"
verbose: true
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ endif
e2e-pat: ## Runs e2e tests. Requires GITHUB_AUTH_TOKEN env var to be set to GitHub personal access token
e2e-pat: build-scorecard check-env | $(GINKGO)
# Run e2e tests. GITHUB_AUTH_TOKEN with personal access token must be exported to run this
TOKEN_TYPE="PAT" $(GINKGO) --race -p -v -cover -coverprofile=e2e-coverage.out --keep-separate-coverprofiles ./...
TOKEN_TYPE="PAT" $(GINKGO) --race -p -v -coverprofile=e2e-coverage.out -coverpkg=./... -r ./...

e2e-gh-token: ## Runs e2e tests. Requires GITHUB_AUTH_TOKEN env var to be set to default GITHUB_TOKEN
e2e-gh-token: build-scorecard check-env | $(GINKGO)
Expand Down Expand Up @@ -450,4 +450,4 @@ cron-github-server-ko: | $(KO) $(KOCACHE_PATH)
--tags latest,$(GIT_VERSION),$(GIT_HASH) \
github.com/ossf/scorecard/v4/clients/githubrepo/roundtripper/tokens/server

###############################################################################
###############################################################################
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OpenSSF Scorecard

[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/ossf/scorecard/badge)](https://api.securityscorecards.dev/projects/github.com/ossf/scorecard)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/ossf/scorecard/badge)](https://securityscorecards.dev/viewer/?uri=github.com/ossf/scorecard)
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/5621/badge)](https://bestpractices.coreinfrastructure.org/projects/5621)
![build](https://github.com/ossf/scorecard/workflows/build/badge.svg?branch=main)
![CodeQL](https://github.com/ossf/scorecard/workflows/CodeQL/badge.svg?branch=main)
Expand Down Expand Up @@ -154,12 +154,12 @@ in the Scorecard GitHub Action setting.

Enabling [`publish_results: true`](https://github.com/ossf/scorecard-action/blob/dd5015aaf9688596b0e6d11e7f24fff566aa366b/action.yaml#L35)
in Scorecard GitHub Actions also allows maintainers to display a Scorecard badge on their repository to show off their
hard work. This badge also auto-updates for every change made to the repository.
hard work. This badge also auto-updates for every change made to the repository. See more details on [this OSSF blogpost](https://openssf.org/blog/2022/09/08/show-off-your-security-score-announcing-scorecards-badges/).

To include a badge on your project's repository, simply add the following markdown to your README:

```
[![OpenSSF
Scorecard](https://api.securityscorecards.dev/projects/github.com/{owner}/{repo}/badge)](https://api.securityscorecards.dev/projects/github.com/{owner}/{repo})
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/{owner}/{repo}/badge)](https://securityscorecards.dev/viewer/?uri=github.com/{owner}/{repo})
```

### Scorecard Command Line Interface
Expand Down
17 changes: 6 additions & 11 deletions checks/evaluation/permissions/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ func TokenPermissions(name string, c *checker.CheckRequest, r *checker.TokenPerm

if score != checker.MaxResultScore {
return checker.CreateResultWithScore(name,
"non read-only tokens detected in GitHub workflows", score)
"detected GitHub workflow tokens with excessive permissions", score)
}

return checker.CreateMaxScoreResult(name,
"tokens are read-only in GitHub workflows")
"GitHub workflow tokens follow principle of least privilege")
}

func applyScorePolicy(results *checker.TokenPermissionsData, c *checker.CheckRequest) (int, error) {
Expand Down Expand Up @@ -325,21 +325,21 @@ func calculateScore(result map[string]permissions) int {
// status: https://docs.github.com/en/rest/reference/repos#statuses.
// May allow an attacker to change the result of pre-submit and get a PR merged.
// Low risk: -0.5.
if permissionIsPresent(perms, "statuses") {
if permissionIsPresentInTopLevel(perms, "statuses") {
score -= 0.5
}

// checks.
// May allow an attacker to edit checks to remove pre-submit and introduce a bug.
// Low risk: -0.5.
if permissionIsPresent(perms, "checks") {
if permissionIsPresentInTopLevel(perms, "checks") {
score -= 0.5
}

// secEvents.
// May allow attacker to read vuln reports before patch available.
// Low risk: -1
if permissionIsPresent(perms, "security-events") {
if permissionIsPresentInTopLevel(perms, "security-events") {
score--
}

Expand All @@ -348,7 +348,7 @@ func calculateScore(result map[string]permissions) int {
// and tiny chance an attacker can trigger a remote
// service with code they own if server accepts code/location var unsanitized.
// Low risk: -1
if permissionIsPresent(perms, "deployments") {
if permissionIsPresentInTopLevel(perms, "deployments") {
score--
}

Expand Down Expand Up @@ -386,11 +386,6 @@ func calculateScore(result map[string]permissions) int {
return int(score)
}

func permissionIsPresent(perms permissions, name string) bool {
return permissionIsPresentInTopLevel(perms, name) ||
permissionIsPresentInRunLevel(perms, name)
}

func permissionIsPresentInTopLevel(perms permissions, name string) bool {
_, ok := perms.topLevelWritePermissions[name]
return ok
Expand Down
18 changes: 17 additions & 1 deletion checks/evaluation/pinned_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,22 @@ func PinningDependencies(name string, c *checker.CheckRequest,
return checker.CreateRuntimeErrorResult(name, err)
}

// Npm installs.
npmScore, err := createReturnForIsNpmInstallPinned(pr, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}

// Scores may be inconclusive.
actionScore = maxScore(0, actionScore)
dockerFromScore = maxScore(0, dockerFromScore)
dockerDownloadScore = maxScore(0, dockerDownloadScore)
scriptScore = maxScore(0, scriptScore)
pipScore = maxScore(0, pipScore)
npmScore = maxScore(0, npmScore)

score := checker.AggregateScores(actionScore, dockerFromScore,
dockerDownloadScore, scriptScore, pipScore)
dockerDownloadScore, scriptScore, pipScore, npmScore)

if score == checker.MaxResultScore {
return checker.CreateMaxScoreResult(name, "all dependencies are pinned")
Expand Down Expand Up @@ -260,6 +267,15 @@ func createReturnForIsPipInstallPinned(pr map[checker.DependencyUseType]pinnedRe
dl)
}

// Create the result for npm install commands.
func createReturnForIsNpmInstallPinned(pr map[checker.DependencyUseType]pinnedResult,
dl checker.DetailLogger,
) (int, error) {
return createReturnValues(pr, checker.DependencyUseTypeNpmCommand,
"npm installs are pinned",
dl)
}

func createReturnValues(pr map[checker.DependencyUseType]pinnedResult,
t checker.DependencyUseType, infoMsg string,
dl checker.DetailLogger,
Expand Down
38 changes: 27 additions & 11 deletions checks/evaluation/pinned_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func Test_PinningDependencies(t *testing.T) {
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 6,
NumberOfInfo: 7,
NumberOfDebug: 1,
},
},
Expand All @@ -132,12 +132,12 @@ func Test_PinningDependencies(t *testing.T) {
Error: nil,
Score: 6,
NumberOfWarn: 1,
NumberOfInfo: 4,
NumberOfInfo: 5,
NumberOfDebug: 1,
},
},
{
name: "various wanrings",
name: "various warnings",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Expand All @@ -158,9 +158,9 @@ func Test_PinningDependencies(t *testing.T) {
},
expected: scut.TestReturn{
Error: nil,
Score: 2,
Score: 3,
NumberOfWarn: 3,
NumberOfInfo: 2,
NumberOfInfo: 3,
NumberOfDebug: 1,
},
},
Expand All @@ -176,7 +176,7 @@ func Test_PinningDependencies(t *testing.T) {
Error: nil,
Score: 8,
NumberOfWarn: 1,
NumberOfInfo: 5,
NumberOfInfo: 6,
NumberOfDebug: 0,
},
},
Expand All @@ -193,7 +193,7 @@ func Test_PinningDependencies(t *testing.T) {
Error: nil,
Score: 10,
NumberOfWarn: 0,
NumberOfInfo: 6,
NumberOfInfo: 7,
NumberOfDebug: 1,
},
},
Expand All @@ -203,12 +203,12 @@ func Test_PinningDependencies(t *testing.T) {
Error: nil,
Score: 10,
NumberOfWarn: 0,
NumberOfInfo: 6,
NumberOfInfo: 7,
NumberOfDebug: 0,
},
},
{
name: "Validate various wanrings and info",
name: "Validate various warnings and info",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Expand All @@ -229,12 +229,28 @@ func Test_PinningDependencies(t *testing.T) {
},
expected: scut.TestReturn{
Error: nil,
Score: 2,
Score: 3,
NumberOfWarn: 3,
NumberOfInfo: 2,
NumberOfInfo: 3,
NumberOfDebug: 1,
},
},
{
name: "unpinned npm install",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Type: checker.DependencyUseTypeNpmCommand,
},
},
expected: scut.TestReturn{
Error: nil,
Score: 8,
NumberOfWarn: 1,
NumberOfInfo: 6,
NumberOfDebug: 0,
},
},
}

for _, tt := range tests {
Expand Down
21 changes: 17 additions & 4 deletions checks/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestGithubTokenPermissions(t *testing.T) {
filenames: []string{"./testdata/.github/workflows/github-workflow-permissions-run-no-codeql-write.yaml"},
expected: scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore - 1,
Score: checker.MaxResultScore,
NumberOfWarn: 1,
NumberOfInfo: 1,
NumberOfDebug: 4,
Expand Down Expand Up @@ -302,11 +302,11 @@ func TestGithubTokenPermissions(t *testing.T) {
},
},
{
name: "workflow jobs only",
name: "penalize job-level read without top level permissions",
filenames: []string{"./testdata/.github/workflows/github-workflow-permissions-jobs-only.yaml"},
expected: scut.TestReturn{
Error: nil,
Score: 9,
Score: checker.MaxResultScore - 1,
NumberOfWarn: 1,
NumberOfInfo: 4,
NumberOfDebug: 4,
Expand All @@ -317,7 +317,7 @@ func TestGithubTokenPermissions(t *testing.T) {
filenames: []string{"./testdata/.github/workflows/github-workflow-permissions-run-write-codeql-comment.yaml"},
expected: scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore - 1,
Score: checker.MaxResultScore,
NumberOfWarn: 1,
NumberOfInfo: 1,
NumberOfDebug: 4,
Expand Down Expand Up @@ -389,6 +389,19 @@ func TestGithubTokenPermissions(t *testing.T) {
NumberOfDebug: 5,
},
},
{
name: "don't penalize job-level writes",
filenames: []string{
"./testdata/.github/workflows/github-workflow-permissions-run-multiple-writes.yaml",
},
expected: scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 7, // number of job-level write permissions
NumberOfInfo: 1, // read-only top-level permissions
NumberOfDebug: 4, // This is 4 + (number of actions = 0)
},
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
Expand Down
2 changes: 1 addition & 1 deletion checks/sast.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func SAST(c *checker.CheckRequest) checker.CheckResult {
Text: getNonCompliantPRMessage(nonCompliantPRs),
})
score := checker.AggregateScoresWithWeight(map[int]int{sastScore: sastWeight, codeQlScore: codeQlWeight})
return checker.CreateResultWithScore(CheckSAST, "SAST tool detected but not run on all commmits", score)
return checker.CreateResultWithScore(CheckSAST, "SAST tool detected but not run on all commits", score)
default:
return checker.CreateRuntimeErrorResult(CheckSAST, sce.WithMessage(sce.ErrScorecardInternal, "contact team"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2021 OpenSSF 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.
name: write-and-read workflow
on: [push]
permissions: read-all

jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
permissions:
statuses: write
checks: write
security-events: write
deployments: write
contents: write
packages: write
actions: write
steps:
- run: echo "write-and-read workflow"
Loading

0 comments on commit 3a4ad09

Please sign in to comment.