mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-11 00:45:36 +03:00
✨ check for read-only permissions of github token (#534)
* check for read-only permissions of github token * linter * linter * doc * comments * commments * fix * generate checks.mg * update license * linter * comments * license * linter * missing file * linter * license * cleanup
This commit is contained in:
parent
93a47e678d
commit
37d979f79b
@ -57,7 +57,7 @@ This check tries to determine if a project has a set of contributors from multip
|
||||
|
||||
## Frozen-Deps
|
||||
|
||||
This check tries to determine if a project has declared and pinned its dependencies. It works by (1) looking for the following files in the root directory: go.mod, go.sum (Golang), package-lock.json, npm-shrinkwrap.json (Javascript), requirements.txt, pipfile.lock (Python), gemfile.lock (Ruby), cargo.lock (Rust), yarn.lock (package manager), composer.lock (PHP), vendor/, third_party/, third-party/; (2) look for github actions under .github/workflows/ and verifies they are pinned by hash. If one of the files in (1) AND all the dependencies in (2) are pinned, the check succeds. This check does not currently look for docker image pinning and shell script pinning.
|
||||
This check tries to determine if a project has declared and pinned its dependencies. It works by (1) looking for the following files in the root directory: go.mod, go.sum (Golang), package-lock.json, npm-shrinkwrap.json (Javascript), requirements.txt, pipfile.lock (Python), gemfile.lock (Ruby), cargo.lock (Rust), yarn.lock (package manager), composer.lock (PHP), vendor/, third_party/, third-party/; (2) look for github actions under .github/workflows/ and verifies they are pinned by hash; (3) looks for Dockerfiles and verifies FROM dependencies are pinned by hash. If one of the files in (1) AND all the dependencies in (2),(3) are pinned, the check succeds. This check does not currently look for shell script pinning (curl | bash) in scripts, Dockerfiles, Makefiles or system()-like code.
|
||||
|
||||
**Remediation steps**
|
||||
- Declare all your dependencies with specific versions in your package format file (e.g. `package.json` for npm, `requirements.txt` for python). For C/C++, check in the code from a trusted source and add a `README` on the specific version used (and the archive SHA hashes).
|
||||
@ -117,3 +117,17 @@ This check looks for cryptographically signed tags in the last 5 tags. The check
|
||||
- Publish the tag and then sign it with this key.
|
||||
- For GitHub, check out the steps [here](https://docs.github.com/en/github/authenticating-to-github/signing-tags#further-reading).
|
||||
|
||||
## Token-Permissions
|
||||
|
||||
This check tries to determine if a project's GitHub workflows follow the principle of least privilege, i.e. if the GitHub tokens are set read-only by default. The check currently checks that the 'permission' keyword is used and set to read/none for the 'contents' permission for every workflow yaml file. If other permissions are set globally for the entire file, this check fails. Otherwise it succeeds.
|
||||
|
||||
**Remediation steps**
|
||||
- Use: ``` permissions:
|
||||
contents: read
|
||||
``` in all your .yaml files.
|
||||
- If you need more permissions, declare them in the job itself, e.g. ``` jobs: create_commit:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
```
|
||||
|
||||
|
@ -15,6 +15,31 @@
|
||||
# This is the source of truth for all check descriptions and remediation steps.
|
||||
# Run `cd checks/main && go run /main` to generate `checks.json` and `checks.md`.
|
||||
checks:
|
||||
Token-Permissions:
|
||||
description: >-
|
||||
This check tries to determine if a project's GitHub workflows
|
||||
follow the principle of least privilege, i.e. if the GitHub tokens
|
||||
are set read-only by default. The check currently checks that the 'permission'
|
||||
keyword is used and set to read/none for the 'contents' permission for every workflow
|
||||
yaml file. If other permissions are set globally for the entire file, this check fails.
|
||||
Otherwise it succeeds.
|
||||
remediation:
|
||||
- >-
|
||||
Use:
|
||||
```
|
||||
permissions:
|
||||
contents: read
|
||||
```
|
||||
in all your .yaml files.
|
||||
- >-
|
||||
If you need more permissions, declare them in the job itself, e.g.
|
||||
```
|
||||
jobs:
|
||||
create_commit:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
```
|
||||
Security-Policy:
|
||||
description: >-
|
||||
This check tries to determine if a project has published a security
|
||||
@ -50,10 +75,11 @@ checks:
|
||||
(Javascript), requirements.txt, pipfile.lock (Python), gemfile.lock
|
||||
(Ruby), cargo.lock (Rust), yarn.lock (package manager), composer.lock
|
||||
(PHP), vendor/, third_party/, third-party/; (2) look for github actions
|
||||
under .github/workflows/ and verifies they are pinned by hash. If one of
|
||||
the files in (1) AND all the dependencies in (2) are pinned, the check
|
||||
succeds. This check does not currently look for docker image pinning and
|
||||
shell script pinning.
|
||||
under .github/workflows/ and verifies they are pinned by hash; (3) looks
|
||||
for Dockerfiles and verifies FROM dependencies are pinned by hash. If one of
|
||||
the files in (1) AND all the dependencies in (2),(3) are pinned, the check
|
||||
succeds. This check does not currently look for shell script pinning (curl | bash)
|
||||
in scripts, Dockerfiles, Makefiles or system()-like code.
|
||||
remediation:
|
||||
- >-
|
||||
Declare all your dependencies with specific versions in your package
|
||||
|
@ -73,6 +73,7 @@ func TestGithubWorkflowPinning(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
//nolint
|
||||
for _, tt := range tests {
|
||||
tt := tt // Re-initializing variable so it is not changed while executing the closure below
|
||||
l.messages = []string{}
|
||||
|
166
checks/permissions.go
Normal file
166
checks/permissions.go
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2021 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 checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/ossf/scorecard/checker"
|
||||
)
|
||||
|
||||
const CheckPermissions = "Token-Permissions"
|
||||
|
||||
// ErrInvalidGitHubWorkflowFile : Invalid GitHub workflow file.
|
||||
var ErrInvalidGitHubWorkflowFile = errors.New("invalid GitHub workflow file")
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
registerCheck(CheckPermissions, leastPrivilegedTokens)
|
||||
}
|
||||
|
||||
func leastPrivilegedTokens(c *checker.CheckRequest) checker.CheckResult {
|
||||
return CheckFilesContent(CheckPermissions, ".github/workflows/*", true, c, validateGitHubActionTokenPermissions)
|
||||
}
|
||||
|
||||
func validatePermission(key string, value interface{}, path string,
|
||||
logf func(s string, f ...interface{})) (bool, error) {
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
if val == "write" {
|
||||
logf("!! token-permissions/github-token - %v permission set to '%v' in %v", key, val, path)
|
||||
return false, nil
|
||||
}
|
||||
default:
|
||||
return false, ErrInvalidGitHubWorkflowFile
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func validateMapPermissions(values map[interface{}]interface{}, path string,
|
||||
logf func(s string, f ...interface{})) (bool, error) {
|
||||
permissionRead := true
|
||||
var r bool
|
||||
var err error
|
||||
|
||||
// Iterate over the permission, verify keys and values are strings.
|
||||
for k, v := range values {
|
||||
switch key := k.(type) {
|
||||
// String type.
|
||||
case string:
|
||||
if r, err = validatePermission(key, v, path, logf); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !r {
|
||||
permissionRead = false
|
||||
}
|
||||
// Invalid type.
|
||||
default:
|
||||
return false, ErrInvalidGitHubWorkflowFile
|
||||
}
|
||||
}
|
||||
return permissionRead, nil
|
||||
}
|
||||
|
||||
func validateReadPermissions(config map[interface{}]interface{}, path string,
|
||||
logf func(s string, f ...interface{})) (bool, error) {
|
||||
permissionFound := false
|
||||
permissionRead := true
|
||||
var err error
|
||||
|
||||
// Iterate over the values.
|
||||
for key, value := range config {
|
||||
if key != "permissions" {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have found the permissions keyword.
|
||||
permissionFound = true
|
||||
|
||||
// Check the type of our values.
|
||||
switch val := value.(type) {
|
||||
// Empty string is nil type.
|
||||
// It defaults to 'none'
|
||||
case nil:
|
||||
|
||||
// String type.
|
||||
case string:
|
||||
if val != "read-all" && val != "" {
|
||||
logf("!! token-permissions/github-token - permission set to '%v' in %v", val, path)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Map type.
|
||||
case map[interface{}]interface{}:
|
||||
var res bool
|
||||
if res, err = validateMapPermissions(val, path, logf); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !res {
|
||||
permissionRead = false
|
||||
}
|
||||
|
||||
// Invalid type.
|
||||
default:
|
||||
return false, ErrInvalidGitHubWorkflowFile
|
||||
}
|
||||
}
|
||||
|
||||
// Did we find a permission at all?
|
||||
if !permissionFound {
|
||||
logf("!! token-permissions/github-token - no permission defined in %v", path)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return permissionRead, nil
|
||||
}
|
||||
|
||||
// Check file content.
|
||||
func validateGitHubActionTokenPermissions(path string, content []byte,
|
||||
logf func(s string, f ...interface{})) (bool, error) {
|
||||
if len(content) == 0 {
|
||||
return false, ErrEmptyFile
|
||||
}
|
||||
|
||||
var workflow map[interface{}]interface{}
|
||||
var r bool
|
||||
var err error
|
||||
err = yaml.Unmarshal(content, &workflow)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("!! token-permissions - cannot unmarshal file %v\n%v\n%v: %w",
|
||||
path, content, string(content), err)
|
||||
}
|
||||
|
||||
// 1. Check that each file uses 'content: read' only or 'none'.
|
||||
//nolint
|
||||
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#example-1-passing-the-github_token-as-an-input,
|
||||
// https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/,
|
||||
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#modifying-the-permissions-for-the-github_token.
|
||||
if r, err = validateReadPermissions(workflow, path, logf); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
if !r {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// TODO(laurent): 2. Identify github actions that require write and add checks.
|
||||
|
||||
// TODO(laurent): 3. Read a few runs and ensures they have the same permissions.
|
||||
|
||||
return true, nil
|
||||
}
|
145
checks/permissions_test.go
Normal file
145
checks/permissions_test.go
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright 2021 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 checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGithubTokenPermissions(t *testing.T) {
|
||||
t.Parallel()
|
||||
type args struct {
|
||||
Logf func(s string, f ...interface{})
|
||||
Filename string
|
||||
}
|
||||
|
||||
type returnValue struct {
|
||||
Error error
|
||||
Result bool
|
||||
}
|
||||
|
||||
l := log{}
|
||||
tests := []struct {
|
||||
args args
|
||||
want returnValue
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "Write all test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-writeall.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Read all test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-readall.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No permission test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-absent.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Writes test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-writes.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Reads test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-reads.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Nones test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-nones.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "None test",
|
||||
args: args{
|
||||
Filename: "./testdata/github-workflow-permissions-none.yaml",
|
||||
Logf: l.Logf,
|
||||
},
|
||||
want: returnValue{
|
||||
Error: nil,
|
||||
Result: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
//nolint
|
||||
for _, tt := range tests {
|
||||
tt := tt // Re-initializing variable so it is not changed while executing the closure below
|
||||
l.messages = []string{}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var content []byte
|
||||
var err error
|
||||
if tt.args.Filename == "" {
|
||||
content = make([]byte, 0)
|
||||
} else {
|
||||
content, err = ioutil.ReadFile(tt.args.Filename)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot read file: %w", err))
|
||||
}
|
||||
}
|
||||
r, err := validateGitHubActionTokenPermissions(tt.args.Filename, content, tt.args.Logf)
|
||||
|
||||
if !errors.Is(err, tt.want.Error) ||
|
||||
r != tt.want.Result {
|
||||
t.Errorf("TestGithubTokenPermissions:\"%v\": %v (%v,%v) want (%v, %v)",
|
||||
tt.name, tt.args.Filename, r, err, tt.want.Result, tt.want.Error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
21
checks/testdata/github-workflow-permissions-absent.yaml
vendored
Normal file
21
checks/testdata/github-workflow-permissions-absent.yaml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2021 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.
|
||||
name: absent workflow
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "absent workflow"
|
23
checks/testdata/github-workflow-permissions-none.yaml
vendored
Normal file
23
checks/testdata/github-workflow-permissions-none.yaml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright 2021 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.
|
||||
name: none workflow
|
||||
on: [push]
|
||||
|
||||
permissions:
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "none workflow"
|
32
checks/testdata/github-workflow-permissions-nones.yaml
vendored
Normal file
32
checks/testdata/github-workflow-permissions-nones.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2021 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.
|
||||
name: writes workflow
|
||||
on: [push]
|
||||
permissions:
|
||||
actions: none
|
||||
checks: none
|
||||
contents: none
|
||||
deployments: none
|
||||
issues: none
|
||||
packages: none
|
||||
pull-requests: none
|
||||
repository-projects: none
|
||||
security-events: none
|
||||
statuses: none
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "none workflow"
|
22
checks/testdata/github-workflow-permissions-readall.yaml
vendored
Normal file
22
checks/testdata/github-workflow-permissions-readall.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright 2021 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.
|
||||
name: read all workflow
|
||||
on: [push]
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "read all workflow"
|
32
checks/testdata/github-workflow-permissions-reads.yaml
vendored
Normal file
32
checks/testdata/github-workflow-permissions-reads.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2021 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.
|
||||
name: reads workflow
|
||||
on: [push]
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
issues: read
|
||||
packages: read
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "reads workflow"
|
22
checks/testdata/github-workflow-permissions-writeall.yaml
vendored
Normal file
22
checks/testdata/github-workflow-permissions-writeall.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright 2021 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.
|
||||
name: write all workflow
|
||||
on: [push]
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "write all workflow"
|
24
checks/testdata/github-workflow-permissions-writes.yaml
vendored
Normal file
24
checks/testdata/github-workflow-permissions-writes.yaml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright 2021 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.
|
||||
name: write-and-read workflow
|
||||
on: [push]
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
Explore-GitHub-Actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "write-and-read workflow"
|
Loading…
Reference in New Issue
Block a user