Skip to content

Commit

Permalink
✨ Structured results for permissions (#2584)
Browse files Browse the repository at this point in the history
* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* Update checks/evaluation/permissions/GitHubWorkflowPermissionsTopNoWrite.yml

Co-authored-by: Joyce <joycebrumu.u@gmail.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* Update checks/evaluation/permissions/GitHubWorkflowPermissionsStepsNoWrite.yml

Co-authored-by: Joyce <joycebrumu.u@gmail.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>

* Update checks/evaluation/permissions/GitHubWorkflowPermissionsStepsNoWrite.yml

Co-authored-by: Joyce <joycebrumu.u@gmail.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>

* Update checks/evaluation/permissions/GitHubWorkflowPermissionsStepsNoWrite.yml

Co-authored-by: Joyce <joycebrumu.u@gmail.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

---------

Signed-off-by: laurentsimon <laurentsimon@google.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>
Co-authored-by: Joyce <joycebrumu.u@gmail.com>
  • Loading branch information
laurentsimon and joycebrum committed Jan 31, 2023
1 parent 4ebe521 commit 2ea140a
Show file tree
Hide file tree
Showing 55 changed files with 1,563 additions and 330 deletions.
3 changes: 2 additions & 1 deletion attestor/policy/attestation_policy.go
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
sclog "github.com/ossf/scorecard/v4/log"
)

Expand Down Expand Up @@ -170,7 +171,7 @@ func CheckPreventBinaryArtifacts(
logger.Info(
fmt.Sprintf(
"binary detected path:%s type: %v offset:%v",
artifactFile.Path, checker.FileTypeBinary, artifactFile.Offset,
artifactFile.Path, finding.FileTypeBinary, artifactFile.Offset,
),
)
return Fail, nil
Expand Down
53 changes: 17 additions & 36 deletions checker/check_result.go
Expand Up @@ -18,13 +18,14 @@ package checker
import (
"fmt"
"math"

"github.com/ossf/scorecard/v4/finding"
"github.com/ossf/scorecard/v4/rule"
)

type (
// DetailType is the type of details.
DetailType int
// FileType is the type of a file.
FileType int
)

const (
Expand All @@ -49,42 +50,18 @@ const (
DetailDebug
)

const (
// FileTypeNone is a default, not defined.
// FileTypeNone must be `0`.
FileTypeNone FileType = iota
// FileTypeSource is for source code files.
FileTypeSource
// FileTypeBinary is for binary files.
FileTypeBinary
// FileTypeText is for text files.
FileTypeText
// FileTypeURL for URLs.
FileTypeURL
)

// CheckResult captures result from a check run.
//
//nolint:govet
type CheckResult struct {
Name string
Version int
Error error
Details []CheckDetail
Score int
Reason string
}

// Remediation represents a remediation.
type Remediation struct {
// Code snippet for humans.
Snippet string
// Diff for machines.
Diff string
// Help text for humans.
HelpText string
// Help text in markdown format for humans.
HelpMarkdown string
Details []CheckDetail
// Structured results.
Rules []string // TODO(X): add support.
}

// CheckDetail contains information for each detail.
Expand All @@ -98,13 +75,17 @@ type CheckDetail struct {
//
//nolint:govet
type LogMessage struct {
Text string // A short string explaining why the detail was recorded/logged.
Path string // Fullpath to the file.
Type FileType // Type of file.
Offset uint // Offset in the file of Path (line for source/text files).
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
Snippet string // Snippet of code
Remediation *Remediation // Remediation information, if any.
// Structured resuts.
Finding *finding.Finding

// Non-structured results.
Text string // A short string explaining why the detail was recorded/logged.
Path string // Fullpath to the file.
Type finding.FileType // Type of file.
Offset uint // Offset in the file of Path (line for source/text files).
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
Snippet string // Snippet of code
Remediation *rule.Remediation // Remediation information, if any.
}

// CreateProportionalScore creates a proportional score.
Expand Down
11 changes: 6 additions & 5 deletions checker/raw_result.go
Expand Up @@ -18,6 +18,7 @@ import (
"time"

"github.com/ossf/scorecard/v4/clients"
"github.com/ossf/scorecard/v4/finding"
)

// RawResults contains results before a policy
Expand Down Expand Up @@ -286,11 +287,11 @@ type ArchivedStatus struct {
// File represents a file.
type File struct {
Path string
Snippet string // Snippet of code
Offset uint // Offset in the file of Path (line for source/text files).
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
FileSize uint // Total size of file.
Type FileType // Type of file.
Snippet string // Snippet of code
Offset uint // Offset in the file of Path (line for source/text files).
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
FileSize uint // Total size of file.
Type finding.FileType // Type of file.
// TODO: add hash.
}

Expand Down
3 changes: 2 additions & 1 deletion checks/evaluation/binary_artifacts.go
Expand Up @@ -17,6 +17,7 @@ package evaluation
import (
"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
)

// BinaryArtifacts applies the score policy for the Binary-Artifacts check.
Expand All @@ -36,7 +37,7 @@ func BinaryArtifacts(name string, dl checker.DetailLogger,
score := checker.MaxResultScore
for _, f := range r.Files {
dl.Warn(&checker.LogMessage{
Path: f.Path, Type: checker.FileTypeBinary,
Path: f.Path, Type: finding.FileTypeBinary,
Offset: f.Offset,
Text: "binary detected",
})
Expand Down
5 changes: 3 additions & 2 deletions checks/evaluation/ci_tests.go
Expand Up @@ -19,6 +19,7 @@ import (
"strings"

"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/finding"
)

const (
Expand Down Expand Up @@ -85,7 +86,7 @@ func prHasSuccessStatus(r checker.RevisionCIInfo, dl checker.DetailLogger) (bool
if isTest(status.Context) || isTest(status.TargetURL) {
dl.Debug(&checker.LogMessage{
Path: status.URL,
Type: checker.FileTypeURL,
Type: finding.FileTypeURL,
Text: fmt.Sprintf("CI test found: pr: %s, context: %s", r.HeadSHA,
status.Context),
})
Expand All @@ -109,7 +110,7 @@ func prHasSuccessfulCheck(r checker.RevisionCIInfo, dl checker.DetailLogger) (bo
if isTest(cr.App.Slug) {
dl.Debug(&checker.LogMessage{
Path: cr.URL,
Type: checker.FileTypeURL,
Type: finding.FileTypeURL,
Text: fmt.Sprintf("CI test found: pr: %d, context: %s", r.PullRequestNumber,
cr.App.Slug),
})
Expand Down
3 changes: 2 additions & 1 deletion checks/evaluation/dependency_update_tool_test.go
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
scut "github.com/ossf/scorecard/v4/utests"
)

Expand Down Expand Up @@ -95,7 +96,7 @@ func TestDependencyUpdateTool(t *testing.T) {
[dependency-update-tool]
enabled = true
`,
Type: checker.FileTypeSource,
Type: finding.FileTypeSource,
},
},
},
Expand Down
5 changes: 3 additions & 2 deletions checks/evaluation/license.go
Expand Up @@ -17,6 +17,7 @@ package evaluation
import (
"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
)

func scoreLicenseCriteria(f *checker.LicenseFile,
Expand All @@ -25,12 +26,12 @@ func scoreLicenseCriteria(f *checker.LicenseFile,
var score int
msg := checker.LogMessage{
Path: "",
Type: checker.FileTypeNone,
Type: finding.FileTypeNone,
Text: "",
Offset: 1,
}
msg.Path = f.File.Path
msg.Type = checker.FileTypeSource
msg.Type = finding.FileTypeSource
// #1 a license file was found.
score += 6

Expand Down
@@ -0,0 +1,33 @@
# Copyright 2023 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.

short: Checks that GitHub workflows do not have steps with dangerous write permissions
desc: This rule checks that GitHub workflows do not have steps with dangerous write permissions
motivation: >
Even with permissions default set to read, some scopes having write permissions in their steps brings incurs a risk to the project.
By giving write permission to the Actions you call in jobs, an external Action you call could abuse them. Depending on the permissions,
this could let the external Action commit unreviewed code, remove pre-submit checks to introduce a bug.
For more information about the scopes and the vulnerabilities involved, see https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions.
implementation: >
The rule is implemented by checking whether the `permissions` keyword is given non-write permissions for the following
scopes: `statuses`, `checks`, `security-events`, `deployments`, `contents`, `packages`, `actions`.
Write permissions given to recognized packaging actions or commands are allowed and are considered an acceptable risk.
risk: Medium
remediation:
effort: High
text:
- Verify which permissions are needed and consider whether you can reduce them.
markdown:
- Verify which permissions are needed and consider whether you can reduce them.
@@ -0,0 +1,36 @@
# Copyright 2023 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.

short: Checks that GitHub workflows do not have default write permissions
desc: This rule checks that GitHub workflows do not have default write permissions
motivation: >
If no permissions are declared, a workflow's GitHub token's permissions default to write for all scopes.
This include write permissions to push to the repository, to read encrypted secrets, etc.
For more information, see https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token.
implementation: >
The rule is implemented by checking whether the `permissions` keyword is defined at the top of the workflow,
and that no write permissions are given.
risk: High
remediation:
effort: Low
text:
- Visit https://app.stepsecurity.io/secureworkflow/${{ repo }}/${{ workflow }}/${{ branch }}?enable=permissions
- Tick the 'Restrict permissions for GITHUB_TOKEN'
- Untick other options
- "NOTE: If you want to resolve multiple issues at once, you can visit https://app.stepsecurity.io/securerepo instead."
markdown:
- Visit [https://app.stepsecurity.io/secureworkflow](https://app.stepsecurity.io/secureworkflow/${{ repo }}/${{ workflow }}/${{ branch }}?enable=permissions).
- Tick the 'Restrict permissions for GITHUB_TOKEN'
- Untick other options
- "NOTE: If you want to resolve multiple issues at once, you can visit [https://app.stepsecurity.io/securerepo](https://app.stepsecurity.io/securerepo) instead."

0 comments on commit 2ea140a

Please sign in to comment.