├── .codecov.yml ├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── dependency-review.yml │ ├── docker-image.yml │ ├── ghcr-retention.yml │ ├── ghcr.yml │ ├── golangci.yml │ ├── scorecards.yml │ └── tests.yaml ├── .gitignore ├── .golangci.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── RELEASE.md ├── SECURITY.md ├── action.yaml ├── artwork ├── Scorecard_Icon.png ├── Scorecard_Icon.svg ├── Scorecard_Icon_withBG.png └── Scorecard_Icon_withBG.svg ├── cloudbuild-tag.yaml ├── cloudbuild.yaml ├── cmd └── installer │ ├── README.md │ ├── main.go │ └── main_test.go ├── codeql.js ├── docs ├── authentication │ ├── classic-token.md │ └── fine-grained-auth-token.md └── development.md ├── e2e └── README.md ├── github └── github.go ├── go.mod ├── go.sum ├── images ├── actionconfirm.png ├── badge.png ├── configurescantool.png ├── exploreworkflow.png ├── install01.png ├── install02.png ├── install03.png ├── install05.png ├── remediation.png ├── searchingossf.png └── tokenscopes.png ├── install ├── cli │ └── cli.go ├── github │ └── github.go ├── install.go └── options │ ├── flags.go │ └── options.go ├── internal └── scorecard │ ├── format.go │ ├── format_test.go │ └── scorecard.go ├── main.go ├── options ├── env.go ├── options.go ├── options_test.go └── testdata │ ├── bad-data.json │ ├── fork.json │ ├── incorrect.json │ ├── non-fork.json │ └── public.json ├── policies └── template.yml └── signing ├── sign-random-data.txt ├── signing.go ├── signing_test.go └── testdata ├── cosign.bundle ├── invalid-cosign.bundle ├── results.json └── results.sarif /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto # auto compares coverage to the previous base commit 6 | comment: 7 | layout: "reach, diff, flags, files" 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | commit-message: 9 | prefix: ":seedling:" 10 | groups: 11 | github-actions: 12 | patterns: 13 | - "*" 14 | 15 | # Maintain dependencies for Docker 16 | - package-ecosystem: "docker" 17 | directory: / 18 | schedule: 19 | interval: "weekly" 20 | commit-message: 21 | prefix: ":seedling:" 22 | groups: 23 | docker-images: 24 | patterns: 25 | - "*" 26 | 27 | # Maintain dependencies for go 28 | - package-ecosystem: "gomod" 29 | directory: "/" 30 | open-pull-requests-limit: 20 31 | schedule: 32 | interval: "daily" 33 | commit-message: 34 | prefix: ":seedling:" 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '17 19 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript','go' ] 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 50 | 51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 52 | # If this step fails, then you should remove it and run the build manually (see below) 53 | - name: Autobuild 54 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 55 | 56 | # ℹ️ Command-line programs to run using the OS shell. 57 | # 📚 https://git.io/JvXDl 58 | 59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 60 | # and modify them (or add more) to build your code if your project 61 | # uses a compiled language 62 | 63 | #- run: | 64 | # make bootstrap 65 | # make release 66 | 67 | - name: Perform CodeQL Analysis 68 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 69 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 OpenSSF 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 | # SPDX-License-Identifier: Apache-2.0 16 | 17 | # Dependency Review Action 18 | # 19 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 20 | # 21 | # Source repository: https://github.com/actions/dependency-review-action 22 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 23 | name: 'Dependency Review' 24 | on: [pull_request] 25 | 26 | permissions: 27 | contents: read 28 | 29 | jobs: 30 | dependency-review: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Harden Runner 34 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 35 | with: 36 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 37 | 38 | - name: 'Checkout Repository' 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | - name: 'Dependency Review' 41 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 42 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile 19 | -------------------------------------------------------------------------------- /.github/workflows/ghcr-retention.yml: -------------------------------------------------------------------------------- 1 | name: Delete untagged GHCR images 2 | on: 3 | schedule: 4 | - cron: '17 10 * * TUE' # Tuesday morning at 10:17 UTC 5 | workflow_dispatch: 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | delete-untagged: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | packages: write 14 | steps: 15 | - uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 16 | with: 17 | package-name: 'scorecard-action' 18 | package-type: 'container' 19 | # keep official releases 20 | delete-only-untagged-versions: 'true' 21 | # 'latest' counts as a tag, so this won't break the e2e tests 22 | min-versions-to-keep: 0 23 | -------------------------------------------------------------------------------- /.github/workflows/ghcr.yml: -------------------------------------------------------------------------------- 1 | name: Publish GitHub Container Registry 2 | on: 3 | push: 4 | branches: ['main'] 5 | tags: 6 | - v* 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build-and-push-image: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | packages: write 20 | attestations: write 21 | id-token: write 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: Log in to the Container registry 27 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 28 | with: 29 | registry: ${{ env.REGISTRY }} 30 | username: ${{ github.actor }} 31 | password: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | - name: Extract metadata (tags, labels) for Docker 34 | id: meta 35 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 36 | with: 37 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 38 | # pushes to the default branch get labeled latest, otherwise use tag name 39 | tags: | 40 | type=raw,value=latest,enable={{is_default_branch}} 41 | type=ref,event=tag 42 | 43 | - name: Build and push Docker image 44 | id: push 45 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 46 | with: 47 | context: . 48 | push: true 49 | tags: ${{ steps.meta.outputs.tags }} 50 | labels: ${{ steps.meta.outputs.labels }} 51 | 52 | - name: Generate artifact attestation 53 | uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 54 | # only publish attestation for our release builds 55 | if: startsWith(github.ref, 'refs/tags/v') 56 | with: 57 | subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} 58 | subject-digest: ${{ steps.push.outputs.digest }} 59 | push-to-registry: true 60 | -------------------------------------------------------------------------------- /.github/workflows/golangci.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | lint: 13 | name: Run golangci lint 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ ubuntu-latest ] 18 | steps: 19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 21 | with: 22 | go-version-file: go.mod 23 | cache: false # golangci/golangci-lint-action maintains its own cache 24 | - uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2 25 | with: 26 | version: v1.55.2 27 | only-new-issues: true 28 | -------------------------------------------------------------------------------- /.github/workflows/scorecards.yml: -------------------------------------------------------------------------------- 1 | name: Scorecards supply-chain security 2 | on: 3 | workflow_dispatch: 4 | 5 | # Declare default permissions as read only. 6 | permissions: read-all 7 | 8 | jobs: 9 | analysis: 10 | name: Scorecards analysis 11 | runs-on: ubuntu-latest 12 | permissions: 13 | # Needed to upload the results to code-scanning dashboard. 14 | security-events: write 15 | id-token: write 16 | 17 | steps: 18 | - name: "Checkout code" 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | with: 21 | persist-credentials: false 22 | 23 | # This is a pre-submit / pre-release. 24 | - name: "Run analysis" 25 | uses: ossf/scorecard-action@main 26 | with: 27 | results_file: results.sarif 28 | results_format: sarif 29 | publish_results: true 30 | 31 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 32 | # format to the repository Actions tab. 33 | - name: "Upload artifact" 34 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 35 | with: 36 | name: SARIF file 37 | path: results.sarif 38 | retention-days: 5 39 | 40 | # Upload the results to GitHub's code scanning dashboard. 41 | - name: "Upload to code-scanning" 42 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 43 | with: 44 | sarif_file: results.sarif 45 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: CI-Tests 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | unit-tests: 12 | name: Run unit tests 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ ubuntu-latest ] 17 | permissions: 18 | id-token: write # Needed to pick up on signing with a GitHub workflow identity. 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 22 | with: 23 | go-version-file: go.mod 24 | cache: true 25 | - name: Run Go tests 26 | # cannot run tests with race because we are mutating state (setting ENV variables) 27 | run: GITHUB_AUTH_TOKEN=${{ secrets.GITHUB_TOKEN }} go test -covermode=atomic -coverprofile=unit-coverage.out ./... 28 | - name: Upload codecoverage 29 | uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6 30 | with: 31 | files: ./unit-coverage.out 32 | verbose: true 33 | 34 | verify: 35 | name: Run verify 36 | runs-on: ${{ matrix.os }} 37 | strategy: 38 | matrix: 39 | os: [ ubuntu-latest ] 40 | steps: 41 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 43 | with: 44 | go-version-file: go.mod 45 | cache: true 46 | - name: Run Go verify 47 | run: | 48 | go mod tidy && go mod verify 49 | git diff --exit-code 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Testing 2 | unit-coverage.out 3 | scorecard-action 4 | output/ 5 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | run: 3 | concurrency: 6 4 | timeout: 5m 5 | issues: 6 | include: 7 | # revive `package-comments` and `exported` rules. 8 | - EXC0012 9 | - EXC0013 10 | - EXC0014 11 | - EXC0015 12 | # Maximum issues count per one linter. 13 | # Set to 0 to disable. 14 | # Default: 50 15 | max-issues-per-linter: 0 16 | # Maximum count of issues with the same text. 17 | # Set to 0 to disable. 18 | # Default: 3 19 | max-same-issues: 0 20 | new-from-rev: "" 21 | linters: 22 | disable-all: true 23 | enable: 24 | - asciicheck 25 | - bodyclose 26 | - dogsled 27 | - errcheck 28 | - errorlint 29 | - exhaustive 30 | - exportloopref 31 | - gci 32 | - gochecknoinits 33 | - gocognit 34 | - goconst 35 | - gocritic 36 | - gocyclo 37 | - godot 38 | - godox 39 | - goerr113 40 | - gofmt 41 | - gofumpt 42 | - goheader 43 | - goimports 44 | - gomodguard 45 | - goprintffuncname 46 | - gosec 47 | - gosimple 48 | - govet 49 | - ineffassign 50 | - lll 51 | - makezero 52 | - misspell 53 | - nakedret 54 | - nestif 55 | - nolintlint 56 | - paralleltest 57 | - predeclared 58 | - revive 59 | - rowserrcheck 60 | - sqlclosecheck 61 | - staticcheck 62 | - stylecheck 63 | - thelper 64 | - tparallel 65 | - typecheck 66 | - unconvert 67 | - unparam 68 | - unused 69 | - whitespace 70 | - wrapcheck 71 | linters-settings: 72 | errcheck: 73 | check-type-assertions: true 74 | check-blank: true 75 | govet: 76 | enable: 77 | - fieldalignment 78 | godox: 79 | keywords: 80 | - BUG 81 | - FIXME 82 | - HACK 83 | gci: 84 | sections: 85 | - standard 86 | - default 87 | - prefix(github.com/ossf) 88 | gocritic: 89 | enabled-checks: 90 | # Diagnostic 91 | - appendAssign 92 | - argOrder 93 | - badCond 94 | - caseOrder 95 | - codegenComment 96 | - commentedOutCode 97 | - deprecatedComment 98 | - dupArg 99 | - dupBranchBody 100 | - dupCase 101 | - dupSubExpr 102 | - exitAfterDefer 103 | - flagDeref 104 | - flagName 105 | - nilValReturn 106 | - offBy1 107 | - sloppyReassign 108 | - weakCond 109 | - octalLiteral 110 | 111 | # Performance 112 | - appendCombine 113 | - equalFold 114 | - hugeParam 115 | - indexAlloc 116 | - rangeExprCopy 117 | - rangeValCopy 118 | 119 | # Style 120 | - assignOp 121 | - boolExprSimplify 122 | - captLocal 123 | - commentFormatting 124 | - commentedOutImport 125 | - defaultCaseOrder 126 | - docStub 127 | - elseif 128 | - emptyFallthrough 129 | - emptyStringTest 130 | - hexLiteral 131 | - ifElseChain 132 | - methodExprCall 133 | - regexpMust 134 | - singleCaseSwitch 135 | - sloppyLen 136 | - stringXbytes 137 | - switchTrue 138 | - typeAssertChain 139 | - typeSwitchVar 140 | - underef 141 | - unlabelStmt 142 | - unlambda 143 | - unslice 144 | - valSwap 145 | - wrapperFunc 146 | - yodaStyleExpr 147 | 148 | # Opinionated 149 | - builtinShadow 150 | - importShadow 151 | - initClause 152 | - nestingReduce 153 | - paramTypeCombine 154 | - ptrToRefParam 155 | - typeUnparen 156 | - unnecessaryBlock 157 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | # Testing: docker run -e GITHUB_REF=refs/heads/main \ 16 | # -e GITHUB_EVENT_NAME=branch_protection_rule \ 17 | # -e INPUT_RESULTS_FORMAT=sarif \ 18 | # -e INPUT_RESULTS_FILE=results.sarif \ 19 | # -e GITHUB_WORKSPACE=/ \ 20 | # -e INPUT_POLICY_FILE="/policy.yml" \ 21 | # -e INPUT_REPO_TOKEN=$GITHUB_AUTH_TOKEN \ 22 | # -e GITHUB_REPOSITORY="ossf/scorecard" \ 23 | # laurentsimon/scorecard-action:latest 24 | 25 | FROM golang:1.24.3@sha256:4c0a1814a7c6c65ece28b3bfea14ee3cf83b5e80b81418453f0e9d5255a5d7b8 AS builder 26 | WORKDIR /src 27 | ENV CGO_ENABLED=0 28 | COPY go.* ./ 29 | RUN go mod download 30 | COPY . ./ 31 | 32 | FROM builder AS build 33 | ARG TARGETOS 34 | ARG TARGETARCH 35 | RUN CGO_ENABLED=0 make build 36 | 37 | # Need root for GitHub Actions support 38 | FROM gcr.io/distroless/base@sha256:cef75d12148305c54ef5769e6511a5ac3c820f39bf5c8a4fbfd5b76b4b8da843 39 | COPY --from=build /src/scorecard-action / 40 | COPY policies/template.yml /policy.yml 41 | ENTRYPOINT [ "/scorecard-action" ] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Security Scorecard Authors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # NOTE: Keep this in sync with go.mod for ossf/scorecard. 2 | LDFLAGS=-X sigs.k8s.io/release-utils/version.gitVersion=v5.2.1 -X sigs.k8s.io/release-utils/version.gitCommit=ab2f6e92482462fe66246d9e32f642855a691dc1 -w -extldflags \"-static\" 3 | 4 | build: ## Runs go build on repo 5 | # Run go build and generate scorecard executable 6 | CGO_ENABLED=0 go build -o scorecard-action -trimpath -a -tags netgo -buildvcs=false -ldflags '$(LDFLAGS)' 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scorecards' GitHub action 2 | [![CodeQL](https://github.com/ossf/scorecard-action/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/ossf/scorecard-action/actions/workflows/codeql-analysis.yml) 3 | [![codecov](https://codecov.io/gh/ossf/scorecard-action/branch/main/graph/badge.svg?token=MAXISWR53I)](https://codecov.io/gh/ossf/scorecard-action) 4 | [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ossf/scorecard-action/badge)](https://scorecard.dev/viewer/?uri=github.com/ossf/scorecard-action) 5 | 6 | > Official GitHub Action for [OSSF Scorecards](https://github.com/ossf/scorecard). 7 | 8 | The Scorecards GitHub Action is free for all public repositories. Private repositories are supported if they have [GitHub Advanced Security](https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security). Private repositories without GitHub Advanced Security can run Scorecards from the command line by following the [standard installation instructions](https://github.com/ossf/scorecard#using-scorecards-1). 9 | 10 | 11 | ## Breaking changes in v2 12 | 13 | Starting from scorecard-action:v2, `GITHUB_TOKEN` permissions or job permissions needs to include 14 | `id-token: write` for `publish_results: true`. This is needed to access GitHub's 15 | OIDC token which verifies the authenticity of the result when publishing it. See details [here](#publishing-results) 16 | 17 | If publishing results, scorecard-action:v2 also imposes new requirements on both the workflow and the job running the `ossf/scorecard-action` step. For full details see [here](#workflow-restrictions). 18 | ________ 19 | [Installation](#installation) 20 | - [Workflow Setup](#workflow-setup-required) 21 | - [Authentication](#authentication-with-fine-grained-pat-optional) 22 | 23 | [View Results](#view-results) 24 | - [REST API](#rest-api) 25 | - [Scorecard Badge](#scorecard-badge) 26 | - [Code Scanning Alerts](#code-scanning-alerts) 27 | - [Verify Runs](#verify-runs) 28 | - [Troubleshooting](#troubleshooting) 29 | 30 | [Manual Action Setup](#manual-action-setup) 31 | - [Inputs](#inputs) 32 | - [Publishing Results](#publishing-results) 33 | - [Workflow Restrictions](#workflow-restrictions) 34 | - [Uploading Artifacts](#uploading-artifacts) 35 | - [Workflow Example](#workflow-example) 36 | 37 | [Reporting vulnerabilities](#reporting-vulnerabilities) 38 | ________ 39 | 40 | The following GitHub triggers are supported: `push`, `schedule` (default branch only). 41 | 42 | The `pull_request` and `workflow_dispatch` triggers are experimental. 43 | 44 | Running the Scorecard action on a fork repository is not supported. 45 | 46 | GitHub Enterprise repositories are not supported. 47 | 48 | ## Installation 49 | 50 | ### Workflow Setup (Required) 51 | 1. From your GitHub project's main page, click “Security” in the top ribbon. 52 | 53 | ![image](/images/install01.png) 54 | 55 | 2. Select “Code scanning”. 56 | 57 | ![image](/images/install02.png) 58 | 59 | 3. Depending on whether the repository already has a scanning tool configured, you will see "Add tool" or "Configure scanning tool. 60 | 61 | a. If you see "Add tool" click it and skip step `b`. 62 | 63 | ![image](/images/install03.png) 64 | 65 | If you see "Configure scanning tool" proceed to step `b`: 66 | 67 | ![image](/images/configurescantool.png) 68 | 69 | b. After clicking "Configure scanning tool", click "Explore workflows" underneath the "Other tools" section: 70 | 71 | ![image](/images/exploreworkflow.png) 72 | 73 | 5. Choose the "OSSF Scorecard" from the list of workflows below, and then click “Configure”. (To find it faster type "OSSF" in searchbox.) 74 | 75 | ![image](/images/searchingossf.png) 76 | 77 | 6. Commit the changes. (Your button might say "Commit changes..." instead of "Start commit", it does the same thing.) 78 | 79 | ![image](/images/install05.png) 80 | 81 | ### Authentication with Fine-grained PAT (optional) 82 | Scorecard can run successfully with the workflow's default `GITHUB_TOKEN`, which is our recommended approach. 83 | However, Scorecard Action requires additional permissions if you use GitHub's classic [Branch Protection](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) settings and want to see it reflected in your results. 84 | You can read more about how to configure Scorecard Action for these cases [here](/docs/authentication/fine-grained-auth-token.md). 85 | 86 | GitHub's new [Repository Rules](https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) are accessible to Scorecard Action with the workflow's default `GITHUB_TOKEN`. 87 | We recommend new repositories use Repository Rules so they can be read with the default GitHub token. 88 | Repositories that already use classic Branch Protection and wish to see their results without an admin token should consider migrating to Repository Rules. 89 | 90 | ## View Results 91 | 92 | The workflow is preconfigured to run on every repository contribution. After making a code change, you can view the results for the change either through the Scorecard Badge, Code Scanning Alerts or GitHub Workflow Runs. 93 | 94 | ### REST API 95 | Starting with scorecard-action:v2, users can use a REST API to query their latest run results. This requires setting [`publish_results: true`](https://github.com/ossf/scorecard/blob/d13ba3f3355b958d5d62edc47282a2e7ed9fa7c1/.github/workflows/scorecard-analysis.yml#L39) for the action and enabling [`id-token: write`](https://github.com/ossf/scorecard/blob/d13ba3f3355b958d5d62edc47282a2e7ed9fa7c1/.github/workflows/scorecard-analysis.yml#L22) permission for the job (needed to access GitHub OIDC token). The API is available here: https://api.scorecard.dev. 96 | 97 | ### Scorecard Badge 98 | 99 | Starting with scorecard-action:v2, users can add a Scorecard Badge to their README to display the latest status of their Scorecard results. This requires setting [`publish_results: true`](https://github.com/ossf/scorecard/blob/d13ba3f3355b958d5d62edc47282a2e7ed9fa7c1/.github/workflows/scorecard-analysis.yml#L39) for the action and enabling [`id-token: write`](https://github.com/ossf/scorecard/blob/d13ba3f3355b958d5d62edc47282a2e7ed9fa7c1/.github/workflows/scorecard-analysis.yml#L22) permission for the job (needed to access GitHub OIDC token). The badge is updated on every run of scorecard-action and points to the latest result. To add a badge to your README, copy and paste the below line, and replace the {owner} and {repo} parts. 100 | 101 | ``` 102 | [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/{owner}/{repo}/badge)](https://scorecard.dev/viewer/?uri=github.com/{owner}/{repo}) 103 | ``` 104 | 105 | Once this badge is added, clicking on the badge will take users to the latest run result of Scorecard. 106 | 107 | ![image](/images/badge.png) 108 | 109 | ### Code Scanning Alerts 110 | 111 | A list of results is accessible by going in the Security tab and clicking "Code Scanning Alerts" (it can take a couple minutes for the run to complete and the results to show up). Click on the individual alerts for more information, including remediation instructions. You will need to click "Show more" to expand the full remediation instructions. 112 | 113 | ![image](/images/remediation.png) 114 | 115 | ### Verify Runs 116 | The workflow is preconfigured to run on every repository contribution. 117 | 118 | To verify that the Action is running successfully, click the repository's Actions tab to see the status of all recent workflow runs. This tab will also show the logs, which can help you troubleshoot if the run failed. 119 | 120 | ![image](/images/actionconfirm.png) 121 | 122 | ### Troubleshooting 123 | If the run has failed, the most likely reason is an authentication failure. Confirm that the Personal Access Token is saved as an encrypted secret within the same repository (see [Authentication](#authentication)). Also confirm that the PAT is still valid and hasn't expired or been revoked. 124 | 125 | If you have a valid PAT saved as an encrypted secret and the run is still failing, confirm that you have not made any changes to the workflow yaml file that affected the syntax. Review the [workflow example](#workflow-example) and reset to the default values if necessary. 126 | 127 | ## Manual Action Setup 128 | 129 | If you prefer to manually set up the Scorecards GitHub Action, you will need to set up a [workflow file](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions). 130 | 131 | First, [create a new file](https://docs.github.com/en/repositories/working-with-files/managing-files/creating-new-files) in this location: `[yourrepo]/.github/workflows/scorecards.yml`. Then use the input values below. 132 | 133 | 134 | ### Inputs 135 | 136 | | Name | Required | Description | 137 | | ----- | -------- | ----------- | 138 | | `results_file` | yes | The file that contains the results. | 139 | | `results_format` | yes | The format in which to store the results [json \| sarif]. For GitHub's scanning dashboard, select `sarif`. | 140 | | `repo_token` | no | PAT token with repository read access. Follow [these steps](/docs/authentication/fine-grained-auth-token.md) to create it. | 141 | | `publish_results` | recommended | This will allow you to display a badge on your repository to show off your hard work. See details [here](#publishing-results).| 142 | | `file_mode` | no | The method to fetch files from the repository: `archive` or `git` (default `archive`). 143 | 144 | ### Publishing Results 145 | The Scorecard team runs a weekly scan of public GitHub repositories in order to track 146 | the overall security health of the open source ecosystem. The results of the scans are [publicly 147 | available](https://github.com/ossf/scorecard#public-data). 148 | Setting `publish_results: true` replaces the results of the team's weekly scans with your own scan results, 149 | helping us scale by cutting down on repeated workflows and GitHub API requests. 150 | This option is also needed to enable badges on the repository. 151 | 152 | ### Workflow Restrictions 153 | 154 | If [publishing results](#publishing-results), our API [enforces certain rules](https://github.com/ossf/scorecard-webapp/blob/9c2f66d5f6ff56ca4a4ac2fba6ec8dcc5379d31c/app/server/post_results.go#L184-L187) on the producing workflow, which may reject the results and cause the Scorecard Action run to fail. 155 | We understand that this is restrictive, but currently it's necessary to ensure the integrity of our API dataset, since GitHub workflow steps run in the same environment as the job they belong to. 156 | If possible, we will work on making this feature more flexible so we can drop this requirement in the future. 157 | 158 | #### Global workflow restrictions 159 | 160 | * The workflow can't contain top level env vars or defaults. 161 | * No workflow level write permissions. 162 | * Only the job with `ossf/scorecard-action` can use `id-token: write` permissions. 163 | 164 | #### Restrictions on the job containing `ossf/scorecard-action` 165 | * No job level env vars or defaults. 166 | * No containers or services 167 | * The job should run on one of the [Ubuntu hosted runners](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners) 168 | * The steps running in this job must belong to this approved list of GitHub actions. 169 | * "actions/checkout" 170 | * "actions/upload-artifact" 171 | * "github/codeql-action/upload-sarif" 172 | * "ossf/scorecard-action" 173 | * "step-security/harden-runner" 174 | 175 | ### Uploading Artifacts 176 | The Scorecards Action uses the [artifact uploader action](https://github.com/actions/upload-artifact) to upload results in SARIF format to the Actions tab. These results are available to anybody for five days after the run to help with debugging. To disable the upload, comment out the `Upload Artifact` value in the Workflow Example. 177 | 178 | Note: if you disable this option, the results of the Scorecards Action run will be only available to people with write access or more. You can find the results on the Security tab scanning dashboard). 179 | 180 | ### Workflow Example 181 | 182 | Please see our workflow from `ossf/scorecard` for an up-to-date example. 183 | https://github.com/ossf/scorecard/blob/main/.github/workflows/scorecard-analysis.yml 184 | 185 | ## Reporting vulnerabilities 186 | 187 | If you find a vulnerability, please report it to us! 188 | See [SECURITY.md](./SECURITY.md) for more information. 189 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Releasing the scorecard GitHub Action 2 | 3 | This is a draft document to describe the release process for the scorecard 4 | GitHub Action. 5 | 6 | (If there are improvements you'd like to see, please comment on the 7 | [tracking issue](https://github.com/ossf/scorecard-action/issues/33) or issue a 8 | pull request to discuss.) 9 | 10 | - [Tracking](#tracking) 11 | - [Preparing the release](#preparing-the-release) 12 | - [Validate the Action](#validate-the-action) 13 | - [Update the scorecard version](#update-the-scorecard-version) 14 | - [Drafting release notes](#drafting-release-notes) 15 | - [Release](#release) 16 | - [Create a tag](#create-a-tag) 17 | - [Create a GitHub release](#create-a-github-release) 18 | - [Update the starter workflow](#update-the-starter-workflow) 19 | - [Announce](#announce) 20 | 21 | ## Tracking 22 | 23 | As the first task, a Release Manager should open a tracking issue for the 24 | release. 25 | 26 | We don't currently have a template for releasing, but the following 27 | [issue](https://github.com/ossf/scorecard-action/issues/97) is a good example 28 | to draw inspiration from. 29 | 30 | We're not striving for perfection with the template, but the tracking issue 31 | will serve as a reference point to aggregate feedback, so try your best to be 32 | as descriptive as possible. 33 | 34 | ## Preparing the release 35 | 36 | This section covers changes that need to be issued as a pull request and should 37 | be merged before releasing the scorecard GitHub Action. 38 | 39 | ### Validate the Action 40 | 41 | Manually run the workflow [scorecards.yml](https://github.com/ossf/scorecard-action/actions/workflows/scorecards.yml) 42 | and verify that the run succeeds. 43 | 44 | ### Update the scorecard-action version 45 | 46 | NOTE: we have a chicken-and-egg problem where the commit to be used for the release 47 | needs to have the image tag that only gets created *after* the commit is pushed. We 48 | workaround that by pre-selecting and referencing the image tag instead of the SHA which isn't ideal 49 | but workable. 50 | 51 | Pre-select the tag which will be used for the release. For this document, we'll use: `Tag`. 52 | 53 | Update the image tag in [action.yaml](action.yaml) to use `Tag`. 54 | 55 | Example: 56 | 57 | ``` 58 | runs: 59 | using: "docker" 60 | image: "docker://ghcr.io/ossf/scorecard-action:Tag" 61 | ``` 62 | 63 | Create a pull request with this change and merge into `main`. 64 | 65 | ## Drafting release notes 66 | 67 | 68 | 69 | ## Release 70 | 71 | ### Create a tag 72 | 73 | Locally, create a signed tag `Tag` on commitSHA `SHA`: 74 | 75 | ```console 76 | git remote update 77 | git checkout `SHA` 78 | git tag -s -m "v100.0.0" v100.0.0 79 | git push --tags 80 | ``` 81 | 82 | ### Create a GitHub release 83 | 84 | Create a 85 | [GitHub release](https://github.com/ossf/scorecard-action/releases/new) using 86 | the tag you've just created. 87 | 88 | Release title: `` 89 | 90 | The release notes will be the notes you drafted in the previous step. 91 | 92 | Ensure the following fields are up to date: 93 | 94 | - Security contact email 95 | - Primary Category 96 | - Another Category — optional 97 | 98 | Click `Publish release`. 99 | 100 | ## Update the starter workflow 101 | 102 | 1. Open a pull request in the 103 | [starter workflows repo](https://github.com/actions/starter-workflows/tree/main/code-scanning/scorecards.yml) 104 | to update the action's digest to `GH2`. 105 | 106 | 1. Update our documentation's example workflow to use `GH2`. 107 | 108 | 1. Verify on 109 | [GitHub Marketplace](https://github.com/marketplace/actions/ossf-scorecard-action) 110 | that the workflow example contains `GH2`. 111 | 112 | _NOTE: GitHub Marketplace uses the default branch as reference documentation_ 113 | 114 | ## Announce 115 | 116 | 117 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | If you find a significant vulnerability, or evidence of one, 4 | please report it privately. 5 | 6 | We prefer that you use the [GitHub mechanism for privately reporting a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). Under the 7 | [main repository's security tab](https://github.com/ossf/scorecard-action/security), click "Report a vulnerability" to open the advisory form. 8 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | # Action syntax: https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions. 16 | 17 | name: "OSSF Scorecard action" 18 | description: "Run OSSF Scorecard checks and output results in SARIF format" 19 | author: "OSSF - github.com/ossf/scorecard" 20 | 21 | inputs: 22 | results_file: 23 | description: "OUTPUT: Path to file to store results" 24 | required: true 25 | 26 | results_format: 27 | description: "OUTPUT: format of the results [json, sarif]" 28 | required: true 29 | 30 | repo_token: 31 | description: "INPUT: GitHub token with read access" 32 | required: false 33 | default: ${{ github.token }} 34 | 35 | publish_results: 36 | description: "INPUT: Publish results" 37 | required: false 38 | default: false 39 | 40 | file_mode: 41 | description: "INPUT: Method to fetch files from GitHub" 42 | required: false 43 | default: archive 44 | 45 | internal_publish_base_url: 46 | description: "INPUT: Base URL for publishing results. Used for testing." 47 | required: false 48 | default: "https://api.scorecard.dev" 49 | 50 | internal_default_token: 51 | description: "INPUT: Default GitHub token. (Internal purpose only, not intended for developers to set. Used for pull requests configured with a PAT)." 52 | required: false 53 | default: ${{ github.token }} 54 | 55 | branding: 56 | icon: "mic" 57 | color: "white" 58 | 59 | runs: 60 | using: "docker" 61 | image: "docker://ghcr.io/ossf/scorecard-action:v2.4.2" 62 | -------------------------------------------------------------------------------- /artwork/Scorecard_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/artwork/Scorecard_Icon.png -------------------------------------------------------------------------------- /artwork/Scorecard_Icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /artwork/Scorecard_Icon_withBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/artwork/Scorecard_Icon_withBG.png -------------------------------------------------------------------------------- /artwork/Scorecard_Icon_withBG.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cloudbuild-tag.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | steps: 16 | - id: Get tag commit 17 | name: gcr.io/cloud-builders/git 18 | args: ['fetch', '--unshallow', '--tags', 'origin', '$COMMIT_SHA'] 19 | - name: 'gcr.io/cloud-builders/docker' 20 | args: ['build', '.', 21 | '-t', 'gcr.io/openssf/scorecard-action:$TAG_NAME', 22 | '-t', 'gcr.io/openssf/scorecard-action:$COMMIT_SHA', 23 | '-f', 'Dockerfile'] 24 | images: ['gcr.io/openssf/scorecard-action'] 25 | timeout: '1600s' 26 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | steps: 16 | - name: 'gcr.io/cloud-builders/docker' 17 | args: ['build', '.', 18 | '-t', 'gcr.io/openssf/scorecard-action:latest', 19 | '-t', 'gcr.io/openssf/scorecard-action:$COMMIT_SHA', 20 | '-f', 'Dockerfile'] 21 | images: ['gcr.io/openssf/scorecard-action'] 22 | timeout: '1600s' 23 | -------------------------------------------------------------------------------- /cmd/installer/README.md: -------------------------------------------------------------------------------- 1 | # Scorecard GitHub Action installer 2 | 3 | This tool can add the 4 | [scorecard GitHub Action](https://github.com/ossf/scorecard-action) to all 5 | accessible repositories under a given organization. A pull request will be 6 | created so that owners can decide whether or not they want to include the 7 | workflow. 8 | 9 | ## Requirements 10 | 11 | Running this tool requires a Personal Access Token (PAT) with the following scopes: 12 | 13 | - `repo > public_repo` 14 | - `admin:org > read:org` 15 | 16 | Instructions on creating a personal access token can be found 17 | [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). 18 | 19 | ## Usage 20 | 21 | ```console 22 | ❯ go run cmd/installer/main.go --help 23 | 24 | The Scorecard GitHub Action installer simplifies the installation of the 25 | scorecard GitHub Action by creating pull requests through the command line. 26 | 27 | Usage: 28 | --owner example_org [--repos ] [flags] 29 | 30 | Flags: 31 | -h, --help help for --owner 32 | --owner string org/owner to install the scorecard action for 33 | --repos strings repositories to install the scorecard action on 34 | ``` 35 | 36 | Another PAT should also be defined as an organization secret for 37 | `scorecards.yml` using steps listed in 38 | [scorecard-action](https://github.com/ossf/scorecard-action#pat-token-creation). 39 | -------------------------------------------------------------------------------- /cmd/installer/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | package main 18 | 19 | import ( 20 | "log" 21 | 22 | "github.com/ossf/scorecard-action/install/cli" 23 | "github.com/ossf/scorecard-action/install/options" 24 | ) 25 | 26 | func main() { 27 | opts := options.New() 28 | if err := cli.New(opts).Execute(); err != nil { 29 | log.Fatalf("error during command execution: %v", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cmd/installer/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | //nolint 18 | // TODO(lint): Remove nolint directive and fix lint warnings 19 | package main 20 | 21 | import ( 22 | "github.com/google/go-github/v46/github" 23 | ) 24 | 25 | var client *github.Client 26 | 27 | // Currently incomplete 28 | //nolint:lll 29 | // Good reference: https://github.com/google/go-github/blob/887f605dd1f81715a4d4e3983e38450b29833639/github/repos_contents_test.go 30 | // Currently from: https://github.com/google/go-github/blob/master/test/integration/repos_test.go 31 | 32 | // TODO: Add/refactor tests 33 | /* 34 | func Test_OrgWorkflowAdd(t *testing.T) { 35 | client = github.NewClient(nil) 36 | me, _, err := client.Users.Get(context.Background(), "") 37 | if err != nil { 38 | t.Fatalf("Users.Get('') returned error: %v", err) 39 | } 40 | 41 | repo, err := createRandomTestRepository(*me.Login, false) 42 | if err != nil { 43 | t.Fatalf("createRandomTestRepository returned error: %v", err) 44 | } 45 | 46 | // update the repository description 47 | repo.Description = github.String("description") 48 | repo.DefaultBranch = nil // FIXME: this shouldn't be necessary 49 | _, _, err = client.Repositories.Edit(context.Background(), *repo.Owner.Login, *repo.Name, repo) 50 | if err != nil { 51 | t.Fatalf("Repositories.Edit() returned error: %v", err) 52 | } 53 | 54 | // delete the repository 55 | _, err = client.Repositories.Delete(context.Background(), *repo.Owner.Login, *repo.Name) 56 | if err != nil { 57 | t.Fatalf("Repositories.Delete() returned error: %v", err) 58 | } 59 | 60 | // verify that the repository was deleted 61 | _, resp, err := client.Repositories.Get(context.Background(), *repo.Owner.Login, *repo.Name) 62 | if err == nil { 63 | t.Fatalf("Test repository still exists after deleting it.") 64 | } 65 | if err != nil && resp.StatusCode != http.StatusNotFound { 66 | t.Fatalf("Repositories.Get() returned error: %v", err) 67 | } 68 | } 69 | 70 | func createRandomTestRepository(owner string, autoinit bool) (*github.Repository, error) { 71 | // create random repo name that does not currently exist 72 | var repoName string 73 | for { 74 | repoName = fmt.Sprintf("test-1") 75 | _, resp, err := client.Repositories.Get(context.Background(), owner, repoName) 76 | if err != nil { 77 | if resp.StatusCode == http.StatusNotFound { 78 | // found a non-existent repo, perfect 79 | break 80 | } 81 | 82 | return nil, err 83 | } 84 | } 85 | 86 | // create the repository 87 | repo, _, err := client.Repositories.Create( 88 | context.Background(), 89 | "", 90 | &github.Repository{ 91 | Name: github.String(repoName), 92 | AutoInit: github.Bool(autoinit), 93 | }, 94 | ) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | return repo, nil 100 | } 101 | */ 102 | -------------------------------------------------------------------------------- /codeql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 OpenSSF Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | console.log("codeql") 20 | -------------------------------------------------------------------------------- /docs/authentication/classic-token.md: -------------------------------------------------------------------------------- 1 | # "Classic" Personal Access Token (PAT) Requirements and Risks 2 | Certain features require a Personal Access Token (PAT). 3 | We recommend you use a fine-grained token as described in [Authentication with Fine-grained PAT](/docs/authentication/fine-grained-auth-token.md). 4 | A "classic" PAT also works, but we strongly discourage its use. 5 | 6 | Due to a limitation of the "classic" tokens' permission model, 7 | the PAT needs [write permission to the repository](https://docs.github.com/developers/apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes) through the `repo` scope. 8 | **The PAT will be stored as a [GitHub encrypted secret](https://docs.github.com/actions/security-guides/encrypted-secrets) 9 | and be accessible by all of the repository's workflows and maintainers.** 10 | This means another maintainer on your project could potentially use the token to impersonate you. 11 | If there is an exploitable bug in a workflow with write permissions, 12 | an external contributor could potentially exploit it to extract the PAT. 13 | 14 | The only benefit of a "classic" PAT is that it can be set to never expire. 15 | However, we believe this does not outweigh the significantly higher risk of "classic" PATs compared to fine-grained PATs. 16 | -------------------------------------------------------------------------------- /docs/authentication/fine-grained-auth-token.md: -------------------------------------------------------------------------------- 1 | # Authentication with Fine-grained PAT (optional) 2 | 3 | For repositories that want to detect their classic Branch Protection rules, or webhooks, we suggest you create a fine-grained Personal Access Token (PAT) that Scorecard may use for authentication. 4 | 5 | 1. [Create a fine-grained Personal Access Token](https://github.com/settings/personal-access-tokens/new) with the following settings: 6 | - Token name: `OpenSSF Scorecard Action - $USER_NAME/$REPO_NAME>` 7 | (Note: replace `$USER_NAME/$REPO_NAME` with the names of your organization and repository so you can keep track of your tokens.) 8 | - Expiration: Set `Custom` and then set the date to exactly a year in the future (the maximum allowed) 9 | - Repository Access: `Only select repositories` and select the desired repository. 10 | Alternatively, set `All repositories` if you wish to use the same token for all your repositories. 11 | - Repository Permissions: 12 | * `Administration: Read-only`: Required to read [Branch-Protection](https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection) settings. 13 | * `Metadata: Read-only` will be automatically set when you set `Administration` 14 | * `Webhooks: Read-only`: (Optional) required for the experimental [Webhook](https://github.com/ossf/scorecard/blob/main/docs/checks.md#webhooks) check. 15 | 16 | **Disclaimer:** Scorecard uses these permissions solely to learn about the project's branch protection rules and webhooks. 17 | However, the token can read many of the project's settings 18 | (for a full list, see the queries marked `(read)` in [GitHub's documentation](https://docs.github.com/en/rest/overview/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28#administration)). 19 | 20 | "Classic" tokens with `repo` scope also work. 21 | However, these carry significantly higher risks compared to fine-grained PATs 22 | (see ["Classic" Personal Access Token (PAT) Requirements and Risks](/docs/authentication/classic-token.md)) 23 | and are therefore strongly discouraged. 24 | 25 | ![image](/images/tokenscopes.png) 26 | 27 | 2. Copy the token value. 28 | 29 | 3. [Create a new repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) with the following settings (**Warning:** [GitHub encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) are accessible by all the workflows and maintainers of a repository.): 30 | - Name: `SCORECARD_TOKEN` 31 | - Value: the value of the token created in step 1 above. 32 | 33 | Note that fine-grained tokens expire after one year. You'll receive an email from GitHub when your token is about to expire, at which point you must regenerate it. Make sure to update the token string in your repository's secrets. 34 | 35 | 4. When you call the `ossf/scorecard-action` in your workflow, pass the token as `repo_token: ${{ secrets.SCORECARD_TOKEN }}`. -------------------------------------------------------------------------------- /e2e/README.md: -------------------------------------------------------------------------------- 1 | # What 2 | 3 | e2e Scorecard action tests for differences in functionality between Scorecard 4 | action implemented in Bash and the updated version implemented using Golang. 5 | These e2e tests will be used until the release of Scorecard Golang action after 6 | which these tests will be modified to run regular e2e testing. 7 | 8 | # Setup 9 | 10 | For testing functionality difference between the 2 implementations, we need a 11 | setup which can invoke these implementations through a GitHub Action on the same 12 | repo/commitSHA. We achieve this by: 13 | 14 | 1. The 2 implementations are built using 2 separate Dockerfiles. `./Dockerfile` 15 | for Bash and `./Dockerfile.golang` for Golang. 16 | 2. A CloudBuild trigger uses `./cloudbuild.yaml` to continuously build and 17 | generate the Golang Docker image. This also helps reduce run time during the 18 | actual GitHub Action run. The generated Docker image is tagged 19 | `scorecard-action:latest`. 20 | 3. Bash implementation at `HEAD` is invoked by referencing: `uses: 21 | ossf/scorecard-action@main` in a GitHub workflow file. 22 | 4. The same repository invokes Golang implementation by referencing: `uses: 23 | gcr.io/openssf/scorecard-action:latest` 24 | 5. The artifact (SARIF file) produced by these 2 implementations are diff-ed to 25 | verify functional similarity. This step is not yet automated and is largely 26 | manual. 27 | 28 | # e2e tests 29 | 30 | The `e2e` tests for the action is run by running the action every day on a cron 31 | for different use cases. The action that run points to `@main` which helps in 32 | catching issues sooner. 33 | 34 | If these actions fails to run these actions would create an issue in the 35 | repository using https://github.com/naveensrinivasan/Create-GitHub-Issue 36 | 37 | The actions primarily run out of https://github.com/ossf-tests organization. 38 | 39 | ## Status 40 | 41 | Testcase | Action | Repository | Status. 42 | ------------------ | ------ | --------------------------------------------------------------------------- | ------- 43 | Fork | Bash | https://github.com/ossf-tests/scorecard-action | [![Fork](https://github.com/ossf-tests/scorecard-action/actions/workflows/scorecards-bash.yml/badge.svg)](https://github.com/ossf-tests/scorecard-action/actions/workflows/scorecards-bash.yml) 44 | Fork | Golang | https://github.com/ossf-tests/scorecard-action | [![Fork](https://github.com/ossf-tests/scorecard-action/actions/workflows/scorecards-golang.yml/badge.svg)](https://github.com/ossf-tests/scorecard-action/actions/workflows/scorecards-golang.yml) 45 | Non-main-branch | Bash | https://github.com/ossf-tests/scorecard-action-non-main-branch | [![non-main-branch](https://github.com/ossf-tests/scorecard-action-non-main-branch/actions/workflows/scorecards-bash.yml/badge.svg)](https://github.com/ossf-tests/scorecard-action-non-main-branch/actions/workflows/scorecards-bash.yml) 46 | Non-main-branch | Golang | https://github.com/ossf-tests/scorecard-action-non-main-branch | [![non-main-branch](https://github.com/ossf-tests/scorecard-action-non-main-branch/actions/workflows/scorecards-golang.yml/badge.svg)](https://github.com/ossf-tests/scorecard-action-non-main-branch/actions/workflows/scorecards-golang.yml) 47 | Private repository | Bash | https://github.com/test-organization-ls/scorecard-action-private-repo-tests | [![Scorecards supply-chain security](https://github.com/test-organization-ls/scorecard-action-private-repo-tests/actions/workflows/scorecard.yml/badge.svg)](https://github.com/test-organization-ls/scorecard-action-private-repo-tests/actions/workflows/scorecard.yml) 48 | Private repository | Golang | https://github.com/test-organization-ls/scorecard-action-private-repo-tests | [![Scorecards supply-chain security](https://github.com/test-organization-ls/scorecard-action-private-repo-tests/actions/workflows/scorecards-golang.yml/badge.svg)](https://github.com/test-organization-ls/scorecard-action-private-repo-tests/actions/workflows/scorecards-golang.yml) 49 | 50 | ## Diff between golang-staging branch and main 51 | 52 | - Here is the sarif results diff between main and golang-staging. There are 53 | few text diffs 54 | https://github.com/ossf-tests/scorecard-action-results/pull/1/files. The PR 55 | is for golang run results. The `main` branch has the `scorecard-action` 56 | `main` branch run results. 57 | 58 | ## Steps to add a new test case 59 | 60 | 1. Create a new repository in the `ossf-tests` organization 61 | 2. Clone this workflow 62 | https://github.com/ossf-tests/scorecard-action-non-main-branch/blob/other/.github/workflows/scorecard-analysis.yml 63 | which has the steps to create an issue if the action fails to run. If the 64 | action fails it should create an issue like this 65 | https://github.com/ossf/scorecard-action/issues/147 66 | -------------------------------------------------------------------------------- /github/github.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenSSF 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 | // Package github provides repo information from GitHub. 16 | package github 17 | 18 | import ( 19 | "bytes" 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "io" 24 | "log" 25 | "net/http" 26 | "net/url" 27 | "os" 28 | 29 | "github.com/ossf/scorecard/v5/clients/githubrepo/roundtripper" 30 | sclog "github.com/ossf/scorecard/v5/log" 31 | ) 32 | 33 | // RepoInfo is a struct for repository information. 34 | type RepoInfo struct { 35 | Repo repo `json:"repository"` 36 | respBytes []byte 37 | } 38 | 39 | type repo struct { 40 | /* 41 | https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#github_repository_is_fork 42 | 43 | GITHUB_REPOSITORY_IS_FORK is true if the repository is a fork. 44 | */ 45 | DefaultBranch *string `json:"default_branch"` 46 | Fork *bool `json:"fork"` 47 | Private *bool `json:"private"` 48 | } 49 | 50 | // Client holds a context and roundtripper for querying repo info from GitHub. 51 | type Client struct { 52 | ctx context.Context 53 | rt http.RoundTripper 54 | } 55 | 56 | // SetContext sets a context for a GitHub client. 57 | func (c *Client) SetContext(ctx context.Context) { 58 | c.ctx = ctx 59 | } 60 | 61 | // SetTransport sets a http.RoundTripper for a GitHub client. 62 | func (c *Client) SetTransport(rt http.RoundTripper) { 63 | c.rt = rt 64 | } 65 | 66 | // Transport returns the http.RoundTripper for a GitHub client. 67 | func (c *Client) Transport() http.RoundTripper { 68 | return c.rt 69 | } 70 | 71 | // SetDefaultTransport sets the scorecard roundtripper for a GitHub client. 72 | func (c *Client) SetDefaultTransport() { 73 | logger := sclog.NewLogger(sclog.DefaultLevel) 74 | rt := roundtripper.NewTransport(c.ctx, logger) 75 | c.rt = rt 76 | } 77 | 78 | // ParseFromURL is a function to get the repository information. 79 | // It is decided to not use the golang GitHub library because of the 80 | // dependency on the github.com/google/go-github/github library 81 | // which will in turn require other dependencies. 82 | func (c *Client) ParseFromURL(baseRepoURL, repoName string) (RepoInfo, error) { 83 | var ret RepoInfo 84 | baseURL, err := url.Parse(baseRepoURL) 85 | if err != nil { 86 | return ret, fmt.Errorf("parsing base repo URL: %w", err) 87 | } 88 | repoURL := baseURL.JoinPath(fmt.Sprintf("repos/%s", repoName)) 89 | 90 | log.Printf("getting repo info from URL: %s", repoURL.String()) 91 | //nolint:noctx 92 | req, err := http.NewRequestWithContext( 93 | c.ctx, 94 | http.MethodGet, 95 | repoURL.String(), 96 | nil /*body*/) 97 | if err != nil { 98 | return ret, fmt.Errorf("error creating request: %w", err) 99 | } 100 | 101 | resp, err := http.DefaultClient.Do(req) 102 | if err != nil { 103 | return ret, fmt.Errorf("error creating request: %w", err) 104 | } 105 | defer resp.Body.Close() 106 | if err != nil { 107 | return ret, fmt.Errorf("error reading response body: %w", err) 108 | } 109 | 110 | respBytes, err := io.ReadAll(resp.Body) 111 | if err != nil { 112 | return ret, fmt.Errorf("error reading response body: %w", err) 113 | } 114 | 115 | prettyPrintJSON(respBytes) 116 | ret.respBytes = respBytes 117 | if err := json.Unmarshal(respBytes, &ret.Repo); err != nil { 118 | return ret, fmt.Errorf("error decoding response body: %w", err) 119 | } 120 | return ret, nil 121 | } 122 | 123 | // ParseFromFile is a function to get the repository information 124 | // from GitHub event file. 125 | func (c *Client) ParseFromFile(filepath string) (RepoInfo, error) { 126 | var ret RepoInfo 127 | 128 | log.Printf("getting repo info from file: %s", filepath) 129 | repoInfo, err := os.ReadFile(filepath) 130 | if err != nil { 131 | return ret, fmt.Errorf("reading GitHub event path: %w", err) 132 | } 133 | 134 | prettyPrintJSON(repoInfo) 135 | if err := json.Unmarshal(repoInfo, &ret); err != nil { 136 | return ret, fmt.Errorf("unmarshalling repo info: %w", err) 137 | } 138 | 139 | return ret, nil 140 | } 141 | 142 | // NewClient returns a new Client for querying repo info from GitHub. 143 | func NewClient(ctx context.Context) *Client { 144 | c := &Client{ 145 | ctx: ctx, 146 | } 147 | 148 | if c.ctx == nil { 149 | c.SetContext(context.Background()) 150 | } 151 | c.SetDefaultTransport() 152 | return c 153 | } 154 | 155 | func prettyPrintJSON(jsonBytes []byte) { 156 | var buf bytes.Buffer 157 | if err := json.Indent(&buf, jsonBytes, "", ""); err != nil { 158 | log.Printf("%v", err) 159 | return 160 | } 161 | log.Println(buf.String()) 162 | } 163 | -------------------------------------------------------------------------------- /images/actionconfirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/actionconfirm.png -------------------------------------------------------------------------------- /images/badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/badge.png -------------------------------------------------------------------------------- /images/configurescantool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/configurescantool.png -------------------------------------------------------------------------------- /images/exploreworkflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/exploreworkflow.png -------------------------------------------------------------------------------- /images/install01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/install01.png -------------------------------------------------------------------------------- /images/install02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/install02.png -------------------------------------------------------------------------------- /images/install03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/install03.png -------------------------------------------------------------------------------- /images/install05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/install05.png -------------------------------------------------------------------------------- /images/remediation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/remediation.png -------------------------------------------------------------------------------- /images/searchingossf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/searchingossf.png -------------------------------------------------------------------------------- /images/tokenscopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/images/tokenscopes.png -------------------------------------------------------------------------------- /install/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | // Package cli contains the root CLI command. 18 | package cli 19 | 20 | import ( 21 | "fmt" 22 | 23 | "github.com/spf13/cobra" 24 | 25 | "github.com/ossf/scorecard-action/install" 26 | "github.com/ossf/scorecard-action/install/options" 27 | ) 28 | 29 | const ( 30 | cmdUsage = `--owner example_org [--repos ]` 31 | cmdDescShort = "Scorecard GitHub Action installer" 32 | cmdDescLong = ` 33 | The Scorecard GitHub Action installer simplifies the installation of the 34 | scorecard GitHub Action by creating pull requests through the command line.` 35 | ) 36 | 37 | // New creates a new instance of the scorecard action installation command. 38 | func New(o *options.Options) *cobra.Command { 39 | cmd := &cobra.Command{ 40 | Use: cmdUsage, 41 | Short: cmdDescShort, 42 | Long: cmdDescLong, 43 | PreRunE: func(cmd *cobra.Command, args []string) error { 44 | err := o.Validate() 45 | if err != nil { 46 | return fmt.Errorf("validating options: %w", err) 47 | } 48 | 49 | return nil 50 | }, 51 | RunE: func(cmd *cobra.Command, args []string) error { 52 | return rootCmd(o) 53 | }, 54 | } 55 | 56 | o.AddFlags(cmd) 57 | return cmd 58 | } 59 | 60 | // rootCmd runs scorecard checks given a set of arguments. 61 | func rootCmd(o *options.Options) error { 62 | err := install.Run(o) 63 | if err != nil { 64 | return fmt.Errorf("running scorecard installation: %w", err) 65 | } 66 | 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /install/github/github.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | // Package github interacts with GitHub repos and orgs. 18 | package github 19 | 20 | import ( 21 | "context" 22 | "fmt" 23 | "log" 24 | "net/http" 25 | 26 | gogh "github.com/google/go-github/v46/github" 27 | 28 | "github.com/ossf/scorecard-action/github" 29 | ) 30 | 31 | // Client is a wrapper around GitHub-related functionality. 32 | type Client struct { 33 | *gogh.Client 34 | } 35 | 36 | // New returns a new GitHub client. 37 | func New(ctx context.Context) *Client { 38 | c := github.NewClient(ctx) 39 | hc := &http.Client{ 40 | Transport: c.Transport(), 41 | } 42 | gh := gogh.NewClient(hc) 43 | client := &Client{gh} 44 | 45 | return client 46 | } 47 | 48 | // Modeled after 49 | // https://github.com/kubernetes-sigs/release-sdk/blob/e23d2c82bbb41a007cdf019c30930e8fd2649c01/github/github.go 50 | 51 | // GetRepositoriesByOrg // TODO(lint): Needs a comment. 52 | func (c *Client) GetRepositoriesByOrg( 53 | ctx context.Context, 54 | owner string, 55 | ) ([]*gogh.Repository, *gogh.Response, error) { 56 | repos, resp, err := c.Repositories.ListByOrg( 57 | ctx, 58 | owner, 59 | // TODO(install): Does this need to parameterized? 60 | &gogh.RepositoryListByOrgOptions{ 61 | Type: "all", 62 | }, 63 | ) 64 | if err != nil { 65 | return repos, resp, fmt.Errorf("getting repositories: %w", err) 66 | } 67 | 68 | return repos, resp, nil 69 | } 70 | 71 | // GetRepository // TODO(lint): Needs a comment. 72 | func (c *Client) GetRepository( 73 | ctx context.Context, 74 | owner, 75 | repo string, 76 | ) (*gogh.Repository, *gogh.Response, error) { 77 | pr, resp, err := c.Repositories.Get(ctx, owner, repo) 78 | if err != nil { 79 | return pr, resp, fmt.Errorf("getting repository: %w", err) 80 | } 81 | 82 | return pr, resp, nil 83 | } 84 | 85 | // GetBranch // TODO(lint): Needs a comment. 86 | func (c *Client) GetBranch( 87 | ctx context.Context, 88 | owner, 89 | repo, 90 | branch string, 91 | followRedirects bool, 92 | ) (*gogh.Branch, *gogh.Response, error) { 93 | // TODO: Revisit logic and simplify returns, where possible. 94 | b, resp, err := c.Repositories.GetBranch( 95 | ctx, 96 | owner, 97 | repo, 98 | branch, 99 | followRedirects, 100 | ) 101 | if err != nil { 102 | return b, resp, fmt.Errorf("getting branch: %w", err) 103 | } 104 | 105 | return b, resp, nil 106 | } 107 | 108 | // GetContents // TODO(lint): Needs a comment. 109 | func (c *Client) GetContents( 110 | ctx context.Context, 111 | owner, 112 | repo, 113 | path string, 114 | opts *gogh.RepositoryContentGetOptions, 115 | ) (*gogh.RepositoryContent, []*gogh.RepositoryContent, *gogh.Response, error) { 116 | // TODO: Revisit logic and simplify returns, where possible. 117 | file, dir, resp, err := c.Repositories.GetContents( 118 | ctx, 119 | owner, 120 | repo, 121 | path, 122 | opts, 123 | ) 124 | if err != nil { 125 | return file, dir, resp, fmt.Errorf("getting repo content: %w", err) 126 | } 127 | 128 | return file, dir, resp, nil 129 | } 130 | 131 | // CreateGitRef // TODO(lint): Needs a comment. 132 | func (c *Client) CreateGitRef( 133 | ctx context.Context, 134 | owner, 135 | repo string, 136 | ref *gogh.Reference, 137 | ) (*gogh.Reference, *gogh.Response, error) { 138 | // TODO: Revisit logic and simplify returns, where possible. 139 | gRef, resp, err := c.Git.CreateRef( 140 | ctx, 141 | owner, 142 | repo, 143 | ref, 144 | ) 145 | if err != nil { 146 | return gRef, resp, fmt.Errorf("creating git reference: %w", err) 147 | } 148 | 149 | return gRef, resp, nil 150 | } 151 | 152 | // CreateFile // TODO(lint): Needs a comment. 153 | func (c *Client) CreateFile( 154 | ctx context.Context, 155 | owner, 156 | repo, 157 | path string, 158 | opts *gogh.RepositoryContentFileOptions, 159 | ) (*gogh.RepositoryContentResponse, *gogh.Response, error) { 160 | // TODO: Revisit logic and simplify returns, where possible. 161 | repoContentResp, resp, err := c.Repositories.CreateFile( 162 | ctx, 163 | owner, 164 | repo, 165 | path, 166 | opts, 167 | ) 168 | if err != nil { 169 | return repoContentResp, resp, fmt.Errorf("creating file: %w", err) 170 | } 171 | 172 | return repoContentResp, resp, nil 173 | } 174 | 175 | // CreatePullRequest // TODO(lint): Needs a comment. 176 | func (c *Client) CreatePullRequest( 177 | ctx context.Context, 178 | owner, 179 | repo, 180 | baseBranchName, 181 | headBranchName, 182 | title, 183 | body string, 184 | ) (*gogh.PullRequest, error) { 185 | newPullRequest := &gogh.NewPullRequest{ 186 | Title: &title, 187 | Head: &headBranchName, 188 | Base: &baseBranchName, 189 | Body: &body, 190 | MaintainerCanModify: gogh.Bool(true), 191 | } 192 | 193 | pr, _, err := c.PullRequests.Create(ctx, owner, repo, newPullRequest) 194 | if err != nil { 195 | return pr, fmt.Errorf("creating pull request: %w", err) 196 | } 197 | 198 | log.Printf( 199 | "successfully created PR #%d for repository %s: %s", 200 | pr.GetNumber(), 201 | repo, 202 | pr.GetHTMLURL(), 203 | ) 204 | 205 | return pr, nil 206 | } 207 | 208 | // CreateGitRefOptions // TODO(lint): Needs a comment. 209 | func CreateGitRefOptions(ref string, sha *string) *gogh.Reference { 210 | return &gogh.Reference{ 211 | Ref: gogh.String(ref), 212 | Object: &gogh.GitObject{SHA: sha}, 213 | } 214 | } 215 | 216 | // CreateRepositoryContentFileOptions // TODO(lint): Needs a comment. 217 | func CreateRepositoryContentFileOptions( 218 | content []byte, 219 | msg, branch string, 220 | ) *gogh.RepositoryContentFileOptions { 221 | return &gogh.RepositoryContentFileOptions{ 222 | Message: gogh.String(msg), 223 | Content: content, 224 | Branch: gogh.String(branch), 225 | } 226 | } 227 | 228 | // CreateRepositoryContentGetOptions // TODO(lint): Needs a comment. 229 | func CreateRepositoryContentGetOptions() *gogh.RepositoryContentGetOptions { 230 | return &gogh.RepositoryContentGetOptions{} 231 | } 232 | -------------------------------------------------------------------------------- /install/install.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | // Package install contains functionality to install the OpenSSF Scorecard workflow. 18 | package install 19 | 20 | import ( 21 | "context" 22 | "fmt" 23 | "log" 24 | "os" 25 | "path" 26 | 27 | "github.com/ossf/scorecard-action/install/github" 28 | "github.com/ossf/scorecard-action/install/options" 29 | ) 30 | 31 | const ( 32 | commitMessage = ".github: Add scorecard workflow" 33 | pullRequestBranch = "scorecard-action-install" 34 | workflowBase = ".github/workflows" 35 | workflowFile = "scorecards.yml" 36 | workflowFileDeprecated = "scorecards-analysis.yml" 37 | ) 38 | 39 | var ( 40 | branchReference = fmt.Sprintf("refs/heads/%s", pullRequestBranch) 41 | pullRequestDescription = `This pull request was generated using the installer tool for scorecard's GitHub Action. 42 | 43 | To report any issues with this tool, see [here](https://github.com/ossf/scorecard-action). 44 | ` 45 | 46 | pullRequestTitle = commitMessage 47 | workflowFilePath = path.Join(workflowBase, workflowFile) 48 | workflowFiles = []string{ 49 | workflowFilePath, 50 | path.Join(workflowBase, workflowFileDeprecated), 51 | } 52 | ) 53 | 54 | // Run adds the OpenSSF Scorecard workflow to all repositories under the given 55 | // organization. 56 | // TODO(install): Improve description. 57 | // TODO(install): Accept a context instead of setting one. 58 | func Run(o *options.Options) error { 59 | err := o.Validate() 60 | if err != nil { 61 | return fmt.Errorf("validating installation options: %w", err) 62 | } 63 | 64 | // Get github user client. 65 | ctx := context.Background() 66 | gh := github.New(ctx) 67 | 68 | // If not provided, get all repositories under organization. 69 | if len(o.Repositories) == 0 { 70 | log.Print("No repositories provided. Fetching all repositories under organization.") 71 | repos, _, err := gh.GetRepositoriesByOrg(ctx, o.Owner) 72 | if err != nil { 73 | return fmt.Errorf("getting repos for owner (%s): %w", o.Owner, err) 74 | } 75 | 76 | // Convert to list of repository names. 77 | for _, repo := range repos { 78 | o.Repositories = append(o.Repositories, *repo.Name) 79 | } 80 | } 81 | 82 | // Get yml file into byte array. 83 | workflowContent, err := os.ReadFile(o.ConfigPath) 84 | if err != nil { 85 | return fmt.Errorf("reading scorecard workflow file: %w", err) 86 | } 87 | 88 | // Process each repository. 89 | // TODO: Capture repo access errors 90 | for _, repoName := range o.Repositories { 91 | log.Printf("Processing repository: %s", repoName) 92 | err := processRepo(ctx, gh, o.Owner, repoName, workflowContent) 93 | if err != nil { 94 | log.Printf("processing repository: %+v", err) 95 | } 96 | 97 | log.Printf( 98 | "finished processing repository %s", 99 | repoName, 100 | ) 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func processRepo( 107 | ctx context.Context, 108 | gh *github.Client, 109 | owner, repoName string, 110 | workflowContent []byte, 111 | ) error { 112 | // Get repo metadata. 113 | log.Printf("getting repo metadata for %s", repoName) 114 | repo, _, err := gh.GetRepository(ctx, owner, repoName) 115 | if err != nil { 116 | return fmt.Errorf( 117 | "getting repository: %w", 118 | err, 119 | ) 120 | } 121 | 122 | // Get head commit SHA of default branch. 123 | // TODO: Capture branch access errors 124 | defaultBranch, _, err := gh.GetBranch( 125 | ctx, 126 | owner, 127 | repoName, 128 | *repo.DefaultBranch, 129 | true, 130 | ) 131 | if err != nil { 132 | return fmt.Errorf( 133 | "getting default branch for %s: %w", 134 | repoName, 135 | err, 136 | ) 137 | } 138 | 139 | defaultBranchSHA := defaultBranch.Commit.SHA 140 | 141 | // Skip if scorecard file already exists in workflows folder. 142 | workflowExists := false 143 | for i, f := range workflowFiles { 144 | log.Printf( 145 | "checking for scorecard workflow file (%s)", 146 | f, 147 | ) 148 | scoreFileContent, _, _, err := gh.GetContents( 149 | ctx, 150 | owner, 151 | repoName, 152 | f, 153 | github.CreateRepositoryContentGetOptions(), 154 | ) 155 | if scoreFileContent != nil { 156 | log.Printf( 157 | "skipping repo (%s) since scorecard workflow already exists: %s", 158 | repoName, 159 | f, 160 | ) 161 | 162 | workflowExists = true 163 | break 164 | } 165 | if err != nil && i == len(workflowFiles)-1 { 166 | log.Printf("could not find a scorecard workflow file: %+v", err) 167 | } 168 | } 169 | 170 | if !workflowExists { 171 | // Skip if branch scorecard already exists. 172 | scorecardBranch, _, err := gh.GetBranch( 173 | ctx, 174 | owner, 175 | repoName, 176 | pullRequestBranch, 177 | true, 178 | ) 179 | if scorecardBranch != nil || err == nil { 180 | log.Printf( 181 | "skipping repo (%s) since the scorecard action installation branch already exists", 182 | repoName, 183 | ) 184 | 185 | return nil 186 | } 187 | 188 | // Create new branch using a reference that stores the new commit hash. 189 | // TODO: Capture ref creation errors 190 | ref := github.CreateGitRefOptions(branchReference, defaultBranchSHA) 191 | _, _, err = gh.CreateGitRef(ctx, owner, repoName, ref) 192 | if err != nil { 193 | return fmt.Errorf( 194 | "creating scorecard action installation branch for %s: %w", 195 | repoName, 196 | err, 197 | ) 198 | } 199 | 200 | // Create file in repository. 201 | // TODO: Capture file creation errors 202 | opts := github.CreateRepositoryContentFileOptions( 203 | workflowContent, 204 | commitMessage, 205 | pullRequestBranch, 206 | ) 207 | _, _, err = gh.CreateFile( 208 | ctx, 209 | owner, 210 | repoName, 211 | workflowFilePath, 212 | opts, 213 | ) 214 | if err != nil { 215 | return fmt.Errorf( 216 | "creating scorecard workflow file for %s: %w", 217 | repoName, 218 | err, 219 | ) 220 | } 221 | 222 | // Create pull request. 223 | // TODO: Capture pull request creation errors 224 | _, err = gh.CreatePullRequest( 225 | ctx, 226 | owner, 227 | repoName, 228 | *defaultBranch.Name, 229 | pullRequestBranch, 230 | pullRequestTitle, 231 | pullRequestDescription, 232 | ) 233 | if err != nil { 234 | return fmt.Errorf( 235 | "creating pull request for %s: %w", 236 | repoName, 237 | err, 238 | ) 239 | } 240 | } 241 | 242 | return nil 243 | } 244 | -------------------------------------------------------------------------------- /install/options/flags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | package options 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | const ( 24 | // FlagOwner is the flag name for specifying an repository owner. 25 | FlagOwner = "owner" 26 | 27 | // FlagRepos is the flag name for specifying a set of repositories. 28 | FlagRepos = "repos" 29 | ) 30 | 31 | // Command is an interface for handling options for command-line utilities. 32 | type Command interface { 33 | // AddFlags adds this options' flags to the cobra command. 34 | AddFlags(cmd *cobra.Command) 35 | } 36 | 37 | // AddFlags adds this options' flags to the cobra command. 38 | func (o *Options) AddFlags(cmd *cobra.Command) { 39 | cmd.Flags().StringVar( 40 | &o.Owner, 41 | FlagOwner, 42 | o.Owner, 43 | "org/owner to install the scorecard action for", 44 | ) 45 | 46 | cmd.Flags().StringSliceVar( 47 | &o.Repositories, 48 | FlagRepos, 49 | o.Repositories, 50 | "repositories to install the scorecard action on", 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /install/options/options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | // Package options provides installation options for the scorecard action. 18 | package options 19 | 20 | import ( 21 | "errors" 22 | "path/filepath" 23 | ) 24 | 25 | const ( 26 | configDir = "starter-workflows/code-scanning" 27 | configFilename = "scorecards.yml" 28 | ) 29 | 30 | var errOwnerNotSpecified = errors.New("owner not specified") 31 | 32 | // Options are installation options for the scorecard action. 33 | type Options struct { 34 | // Scorecard GitHub Action configuration path 35 | ConfigPath string 36 | 37 | // GitHub org/repo owner 38 | Owner string 39 | 40 | // Repositories 41 | Repositories []string 42 | } 43 | 44 | // New creates a new instance of installation options. 45 | func New() *Options { 46 | opts := &Options{} 47 | opts.ConfigPath = GetConfigPath() 48 | return opts 49 | } 50 | 51 | // Validate checks if the installation options specified are valid. 52 | func (o *Options) Validate() error { 53 | if o.Owner == "" { 54 | return errOwnerNotSpecified 55 | } 56 | 57 | return nil 58 | } 59 | 60 | // GetConfigPath returns the local path for the scorecard action config file. 61 | // TODO: Consider making this configurable. 62 | func GetConfigPath() string { 63 | return filepath.Join(configDir, configFilename) 64 | } 65 | -------------------------------------------------------------------------------- /internal/scorecard/format.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 OpenSSF 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 | package scorecard 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "io" 21 | "os" 22 | "path/filepath" 23 | "strings" 24 | 25 | "github.com/ossf/scorecard-action/options" 26 | "github.com/ossf/scorecard/v5/docs/checks" 27 | sclog "github.com/ossf/scorecard/v5/log" 28 | "github.com/ossf/scorecard/v5/pkg/scorecard" 29 | "github.com/ossf/scorecard/v5/policy" 30 | ) 31 | 32 | const ( 33 | defaultScorecardPolicyFile = "/policy.yml" 34 | ) 35 | 36 | var ( 37 | errNoResult = errors.New("must provide a result") 38 | errUnknownFormat = errors.New("unknown result format") 39 | ) 40 | 41 | // Format provides a wrapper around the Scorecard library's various formatting functions, 42 | // converting our options into theirs. 43 | func Format(result *scorecard.Result, opts *options.Options) error { 44 | if result == nil { 45 | return errNoResult 46 | } 47 | 48 | // write results to both stdout and result file 49 | resultFile, err := os.Create(filepath.Join(opts.GithubWorkspace, opts.InputResultsFile)) 50 | if err != nil { 51 | return fmt.Errorf("creating result file: %w", err) 52 | } 53 | defer resultFile.Close() 54 | writer := io.MultiWriter(resultFile, os.Stdout) 55 | 56 | docs, err := checks.Read() 57 | if err != nil { 58 | return fmt.Errorf("read check docs: %w", err) 59 | } 60 | 61 | switch strings.ToLower(opts.InputResultsFormat) { 62 | // sarif is considered the default format when unset 63 | case "", "sarif": 64 | if opts.ScorecardOpts.PolicyFile == "" { 65 | opts.ScorecardOpts.PolicyFile = defaultScorecardPolicyFile 66 | } 67 | pol, err := policy.ParseFromFile(opts.ScorecardOpts.PolicyFile) 68 | if err != nil { 69 | return fmt.Errorf("parse policy file: %w", err) 70 | } 71 | err = result.AsSARIF(true, sclog.DefaultLevel, writer, docs, pol, opts.ScorecardOpts) 72 | if err != nil { 73 | return fmt.Errorf("format as sarif: %w", err) 74 | } 75 | case "json": 76 | err = result.AsJSON2(writer, docs, &scorecard.AsJSON2ResultOption{ 77 | Details: true, 78 | Annotations: false, // TODO 79 | LogLevel: sclog.DefaultLevel, 80 | }) 81 | if err != nil { 82 | return fmt.Errorf("format as JSON: %w", err) 83 | } 84 | default: 85 | return errUnknownFormat 86 | } 87 | 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /internal/scorecard/format_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 OpenSSF 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 | package scorecard 16 | 17 | import ( 18 | "bytes" 19 | "os" 20 | "testing" 21 | 22 | "github.com/ossf/scorecard-action/options" 23 | scopts "github.com/ossf/scorecard/v5/options" 24 | "github.com/ossf/scorecard/v5/pkg/scorecard" 25 | ) 26 | 27 | func TestFormat(t *testing.T) { 28 | t.Parallel() 29 | tests := []struct { 30 | name, format string 31 | pattern []byte 32 | }{ 33 | { 34 | name: "default is sarif", 35 | format: "", 36 | pattern: []byte("sarif-schema"), 37 | }, 38 | { 39 | name: "sarif format supported", 40 | format: "sarif", 41 | pattern: []byte("sarif-schema"), 42 | }, 43 | { 44 | name: "json format supported", 45 | format: "json", 46 | // This isn't quite as strong of a guarantee, but dont expect this to change 47 | pattern: []byte(`"name":"github.com/foo/bar"`), 48 | }, 49 | { 50 | name: "format is case insensitive", 51 | format: "SARIF", 52 | pattern: []byte("sarif-schema"), 53 | }, 54 | } 55 | result := scorecard.Result{ 56 | Repo: scorecard.RepoInfo{ 57 | Name: "github.com/foo/bar", 58 | }, 59 | } 60 | for _, tt := range tests { 61 | tt := tt 62 | t.Run(tt.name, func(t *testing.T) { 63 | t.Parallel() 64 | opts := options.Options{ 65 | InputResultsFile: t.TempDir() + "/results", 66 | InputResultsFormat: tt.format, 67 | ScorecardOpts: &scopts.Options{ 68 | PolicyFile: "../../policies/template.yml", 69 | }, 70 | } 71 | err := Format(&result, &opts) 72 | if err != nil { 73 | t.Fatalf("unexpected error: %v", err) 74 | } 75 | contents, err := os.ReadFile(opts.InputResultsFile) 76 | if err != nil { 77 | t.Fatalf("unexpected error: %v", err) 78 | } 79 | if !bytes.Contains(contents, tt.pattern) { 80 | t.Errorf("Output didn't match expected pattern (%s)", tt.pattern) 81 | } 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /internal/scorecard/scorecard.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 OpenSSF 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 | // Package scorecard provides functionality to run Scorecard and format the results. 16 | package scorecard 17 | 18 | import ( 19 | "context" 20 | "errors" 21 | "fmt" 22 | "strings" 23 | 24 | "github.com/ossf/scorecard-action/options" 25 | "github.com/ossf/scorecard/v5/clients" 26 | "github.com/ossf/scorecard/v5/clients/githubrepo" 27 | "github.com/ossf/scorecard/v5/clients/localdir" 28 | sce "github.com/ossf/scorecard/v5/errors" 29 | "github.com/ossf/scorecard/v5/pkg/scorecard" 30 | ) 31 | 32 | // Run provides a wrapper around the Scorecard library's Run function, converting our options into theirs. 33 | func Run(opts *options.Options) (scorecard.Result, error) { 34 | repo, err := getRepo(opts) 35 | if err != nil { 36 | return scorecard.Result{}, fmt.Errorf("unable to create repo: %w", err) 37 | } 38 | 39 | var scOpts []scorecard.Option 40 | if strings.EqualFold(opts.InputFileMode, "git") { 41 | scOpts = append(scOpts, scorecard.WithFileModeGit()) 42 | } 43 | result, err := scorecard.Run(context.Background(), repo, scOpts...) 44 | if err != nil && !errors.Is(err, sce.ErrCheckRuntime) { 45 | return scorecard.Result{}, fmt.Errorf("scorecard had an error: %w", err) 46 | } 47 | return result, nil 48 | } 49 | 50 | //nolint:wrapcheck // just a helper 51 | func getRepo(opts *options.Options) (clients.Repo, error) { 52 | if opts.ScorecardOpts.Local != "" { 53 | return localdir.MakeLocalDirRepo(opts.ScorecardOpts.Local) 54 | } 55 | return githubrepo.MakeGithubRepo(opts.ScorecardOpts.Repo) 56 | } 57 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenSSF 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 | // Command scorecard-action is the entrypoint for the Scorecard GitHub Action. 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "log" 21 | "os" 22 | "path/filepath" 23 | 24 | "github.com/ossf/scorecard-action/internal/scorecard" 25 | "github.com/ossf/scorecard-action/options" 26 | "github.com/ossf/scorecard-action/signing" 27 | ) 28 | 29 | func main() { 30 | triggerEventName := os.Getenv("GITHUB_EVENT_NAME") 31 | if triggerEventName == "pull_request_target" { 32 | log.Fatalf("pull_request_target trigger is not supported for security reasons" + 33 | "see https://securitylab.github.com/research/github-actions-preventing-pwn-requests/") 34 | } 35 | 36 | opts, err := getOpts() 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | opts.Print() 41 | 42 | result, err := scorecard.Run(opts) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | if err := scorecard.Format(&result, opts); err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | // `pull_request` does not have the necessary `token-id: write` permissions. 52 | // 53 | //nolint:nestif // trying to keep the refactor simpler 54 | if os.Getenv(options.EnvInputPublishResults) == "true" && triggerEventName != "pull_request" { 55 | // if we don't already have the results as JSON, generate them 56 | if opts.InputResultsFormat != "json" { 57 | opts.InputResultsFormat = "json" 58 | opts.InputResultsFile = "results.json" 59 | err = scorecard.Format(&result, opts) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | } 64 | 65 | resultFile := filepath.Join(opts.GithubWorkspace, opts.InputResultsFile) 66 | jsonPayload, err := os.ReadFile(resultFile) 67 | if err != nil { 68 | log.Fatalf("reading json scorecard results: %v", err) 69 | } 70 | 71 | // Sign json results. 72 | // Always use the default GitHub token, never a PAT. 73 | accessToken := os.Getenv(options.EnvInputInternalRepoToken) 74 | s, err := signing.New(accessToken) 75 | if err != nil { 76 | log.Fatalf("error SigningNew: %v", err) 77 | } 78 | // TODO: does it matter if this is hardcoded as results.json or not? 79 | if err = s.SignScorecardResult(resultFile); err != nil { 80 | log.Fatalf("error signing scorecard json results: %v", err) 81 | } 82 | 83 | // Processes json results. 84 | repoName := os.Getenv(options.EnvGithubRepository) 85 | repoRef := os.Getenv(options.EnvGithubRef) 86 | if err := s.ProcessSignature(jsonPayload, repoName, repoRef); err != nil { 87 | log.Fatalf("error processing signature: %v", err) 88 | } 89 | } 90 | } 91 | 92 | func getOpts() (*options.Options, error) { 93 | opts, err := options.New() 94 | if err != nil { 95 | return nil, fmt.Errorf("creating new options: %w", err) 96 | } 97 | if err := opts.Validate(); err != nil { 98 | return nil, fmt.Errorf("validating options: %w", err) 99 | } 100 | return opts, nil 101 | } 102 | -------------------------------------------------------------------------------- /options/env.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenSSF 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 | package options 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | ) 21 | 22 | // Environment variables. 23 | // TODO(env): Remove once environment variables are not used for config. 24 | // 25 | //nolint:revive,nolintlint 26 | const ( 27 | EnvEnableSarif = "ENABLE_SARIF" 28 | EnvEnableLicense = "ENABLE_LICENSE" 29 | EnvEnableDangerousWorkflow = "ENABLE_DANGEROUS_WORKFLOW" 30 | EnvGithubEventPath = "GITHUB_EVENT_PATH" 31 | EnvGithubEventName = "GITHUB_EVENT_NAME" 32 | EnvGithubRepository = "GITHUB_REPOSITORY" 33 | EnvGithubRef = "GITHUB_REF" 34 | EnvGithubWorkspace = "GITHUB_WORKSPACE" 35 | EnvGithubAuthToken = "GITHUB_AUTH_TOKEN" //nolint:gosec 36 | EnvScorecardFork = "SCORECARD_IS_FORK" 37 | EnvScorecardPrivateRepo = "SCORECARD_PRIVATE_REPOSITORY" 38 | 39 | // TODO(input): INPUT_ constants should be removed in a future release once 40 | // they have replacements in upstream scorecard. 41 | EnvInputRepoToken = "INPUT_REPO_TOKEN" //nolint:gosec 42 | EnvInputInternalRepoToken = "INPUT_INTERNAL_DEFAULT_TOKEN" //nolint:gosec 43 | EnvInputResultsFile = "INPUT_RESULTS_FILE" 44 | EnvInputResultsFormat = "INPUT_RESULTS_FORMAT" 45 | EnvInputPublishResults = "INPUT_PUBLISH_RESULTS" 46 | EnvInputFileMode = "INPUT_FILE_MODE" 47 | EnvInputInternalPublishBaseURL = "INPUT_INTERNAL_PUBLISH_BASE_URL" 48 | ) 49 | 50 | // Errors 51 | 52 | var ( 53 | // Errors. 54 | errEmptyGitHubAuthToken = errEnvVarIsEmptyWithKey(EnvGithubAuthToken) 55 | 56 | errEnvVarIsEmpty = errors.New("env var is empty") 57 | ) 58 | 59 | func errEnvVarIsEmptyWithKey(envVar string) error { 60 | return fmt.Errorf("%w: %s", errEnvVarIsEmpty, envVar) 61 | } 62 | -------------------------------------------------------------------------------- /options/options.go: -------------------------------------------------------------------------------- 1 | // Copyright OpenSSF 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 | package options 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "os" 21 | "strconv" 22 | "strings" 23 | 24 | "github.com/caarlos0/env/v6" 25 | "golang.org/x/net/context" 26 | 27 | "github.com/ossf/scorecard-action/github" 28 | scopts "github.com/ossf/scorecard/v5/options" 29 | ) 30 | 31 | const ( 32 | defaultScorecardPolicyFile = "/policy.yml" 33 | trueStr = "true" 34 | formatSarif = scopts.FormatSarif 35 | 36 | pullRequestEvent = "pull_request" 37 | pushEvent = "push" 38 | branchProtectionEvent = "branch_protection_rule" 39 | ) 40 | 41 | var ( 42 | // Errors. 43 | errGithubEventPathEmpty = errors.New("GitHub event path is empty") 44 | errResultsPathEmpty = errors.New("results path is empty") 45 | errGitHubRepoInfoUnavailable = errors.New("GitHub repo info inaccessible") 46 | errOnlyDefaultBranchSupported = errors.New("only default branch is supported") 47 | ) 48 | 49 | // Options are options for running scorecard via GitHub Actions. 50 | type Options struct { 51 | // Scorecard options. 52 | ScorecardOpts *scopts.Options 53 | 54 | // Scorecard command-line options. 55 | EnabledChecks string `env:"ENABLED_CHECKS"` 56 | 57 | // Scorecard checks. 58 | EnableLicense string `env:"ENABLE_LICENSE"` 59 | EnableDangerousWorkflow string `env:"ENABLE_DANGEROUS_WORKFLOW"` 60 | 61 | // GitHub options. 62 | // TODO(github): Consider making this a separate options set so we can 63 | // encapsulate handling 64 | GithubEventName string `env:"GITHUB_EVENT_NAME"` 65 | GithubEventPath string `env:"GITHUB_EVENT_PATH"` 66 | GithubRef string `env:"GITHUB_REF"` 67 | GithubRepository string `env:"GITHUB_REPOSITORY"` 68 | GithubWorkspace string `env:"GITHUB_WORKSPACE"` 69 | GithubAPIURL string `env:"GITHUB_API_URL"` 70 | 71 | DefaultBranch string `env:"SCORECARD_DEFAULT_BRANCH"` 72 | // TODO(options): This may be better as a bool 73 | IsForkStr string `env:"SCORECARD_IS_FORK"` 74 | // TODO(options): This may be better as a bool 75 | PrivateRepoStr string `env:"SCORECARD_PRIVATE_REPOSITORY"` 76 | 77 | // Input parameters 78 | InputResultsFile string `env:"INPUT_RESULTS_FILE"` 79 | InputResultsFormat string `env:"INPUT_RESULTS_FORMAT"` 80 | InputFileMode string `env:"INPUT_FILE_MODE"` 81 | 82 | PublishResults bool 83 | } 84 | 85 | // New creates a new options set for running scorecard via GitHub Actions. 86 | func New() (*Options, error) { 87 | opts := &Options{} 88 | if err := env.Parse(opts); err != nil { 89 | return opts, fmt.Errorf("parsing entrypoint env vars: %w", err) 90 | } 91 | // GITHUB_AUTH_TOKEN 92 | // Needs to be set *before* setRepoInfo() is invoked. 93 | // setRepoInfo() uses the GITHUB_AUTH_TOKEN env for querying the REST API. 94 | if _, tokenSet := os.LookupEnv(EnvGithubAuthToken); !tokenSet { 95 | inputToken := os.Getenv(EnvInputRepoToken) 96 | os.Setenv(EnvGithubAuthToken, inputToken) 97 | } 98 | if err := opts.setRepoInfo(); err != nil { 99 | return opts, fmt.Errorf("parsing repo info: %w", err) 100 | } 101 | opts.setScorecardOpts() 102 | opts.setPublishResults() 103 | return opts, nil 104 | } 105 | 106 | // Validate validates the scorecard configuration. 107 | func (o *Options) Validate() error { 108 | fmt.Println("EnvGithubAuthToken:", EnvGithubAuthToken, os.Getenv(EnvGithubAuthToken)) 109 | if os.Getenv(EnvGithubAuthToken) == "" { 110 | fmt.Printf("%s variable is empty.\n", EnvGithubAuthToken) 111 | if o.IsForkStr == trueStr { 112 | fmt.Printf("We have detected you are running on a fork.\n") 113 | } 114 | 115 | fmt.Printf( 116 | "Please follow the instructions at https://github.com/ossf/scorecard-action#authentication to create the read-only PAT token.\n", //nolint:lll 117 | ) 118 | 119 | return errEmptyGitHubAuthToken 120 | } 121 | 122 | if !o.isPullRequestEvent() && 123 | !o.isDefaultBranch() { 124 | fmt.Printf("%s not supported with %s event.\n", o.GithubRef, o.GithubEventName) 125 | fmt.Printf("::error ::Only the default branch %s is supported.\n", o.DefaultBranch) 126 | 127 | return errOnlyDefaultBranchSupported 128 | } 129 | if err := o.ScorecardOpts.Validate(); err != nil { 130 | return fmt.Errorf("validating scorecard options: %w", err) 131 | } 132 | if o.ScorecardOpts.ResultsFile == "" { 133 | // TODO(test): Reassess test case for this code path 134 | return errResultsPathEmpty 135 | } 136 | return nil 137 | } 138 | 139 | // Print is a function to print options. 140 | func (o *Options) Print() { 141 | // Scorecard options 142 | fmt.Println("Scorecard options:") 143 | fmt.Printf("Ref: %s\n", o.ScorecardOpts.Commit) 144 | fmt.Printf("Repository: %s\n", o.ScorecardOpts.Repo) 145 | fmt.Printf("Local: %s\n", o.ScorecardOpts.Local) 146 | fmt.Printf("Format: %s\n", o.ScorecardOpts.Format) 147 | fmt.Printf("Policy file: %s\n", o.ScorecardOpts.PolicyFile) 148 | fmt.Println() 149 | fmt.Println("Event / repo information:") 150 | fmt.Printf("Event file: %s\n", o.GithubEventPath) 151 | fmt.Printf("Event name: %s\n", o.GithubEventName) 152 | fmt.Printf("Fork repository: %s\n", o.IsForkStr) 153 | fmt.Printf("Private repository: %s\n", o.PrivateRepoStr) 154 | fmt.Printf("Publication enabled: %+v\n", o.PublishResults) 155 | fmt.Printf("Default branch: %s\n", o.DefaultBranch) 156 | } 157 | 158 | func (o *Options) setScorecardOpts() { 159 | o.ScorecardOpts = scopts.New() 160 | // Set GITHUB_AUTH_TOKEN 161 | inputToken := os.Getenv(EnvInputRepoToken) 162 | if inputToken == "" { 163 | fmt.Printf("The 'repo_token' variable is empty.\n") 164 | fmt.Printf("Using the '%s' variable instead.\n", EnvInputInternalRepoToken) 165 | inputToken := os.Getenv(EnvInputInternalRepoToken) 166 | os.Setenv(EnvGithubAuthToken, inputToken) 167 | } 168 | 169 | // --repo= | --local 170 | // This section restores functionality that was removed in 171 | // https://github.com/ossf/scorecard/pull/1898. 172 | // TODO(options): Consider moving this to its own function. 173 | if !o.isPullRequestEvent() { 174 | o.ScorecardOpts.Repo = o.GithubRepository 175 | } else { 176 | o.ScorecardOpts.Local = "." 177 | } 178 | 179 | // --format= 180 | // Enable scorecard command to use SARIF format (default format). 181 | os.Setenv(scopts.EnvVarEnableSarif, trueStr) 182 | o.ScorecardOpts.EnableSarif = true 183 | o.ScorecardOpts.Format = formatSarif 184 | if o.InputResultsFormat != "" { 185 | o.ScorecardOpts.Format = o.InputResultsFormat 186 | } 187 | if o.ScorecardOpts.Format == formatSarif && o.ScorecardOpts.PolicyFile == "" { 188 | // TODO(policy): Should we default or error here? 189 | o.ScorecardOpts.PolicyFile = defaultScorecardPolicyFile 190 | } 191 | 192 | // --show-details 193 | o.ScorecardOpts.ShowDetails = true 194 | 195 | // --commit= 196 | // TODO(scorecard): Reset commit options. Fix this in scorecard. 197 | o.ScorecardOpts.Commit = scopts.DefaultCommit 198 | 199 | // --out-file= 200 | if o.ScorecardOpts.ResultsFile == "" { 201 | o.ScorecardOpts.ResultsFile = o.InputResultsFile 202 | } 203 | } 204 | 205 | // setPublishResults sets whether results should be published based on a 206 | // repository's visibility. 207 | func (o *Options) setPublishResults() { 208 | inputVal := o.PublishResults 209 | o.PublishResults = false 210 | privateRepo, err := strconv.ParseBool(o.PrivateRepoStr) 211 | if err != nil { 212 | // TODO(options): Consider making this an error. 213 | fmt.Printf( 214 | "parsing bool from %s: %+v\n", 215 | o.PrivateRepoStr, 216 | err, 217 | ) 218 | return 219 | } 220 | 221 | o.PublishResults = inputVal && !privateRepo 222 | } 223 | 224 | // setRepoInfo gets the path to the GitHub event and sets the 225 | // SCORECARD_IS_FORK environment variable. 226 | // TODO(options): Check if this actually needs to be exported. 227 | // TODO(options): Choose a more accurate name for what this does. 228 | func (o *Options) setRepoInfo() error { 229 | eventPath := o.GithubEventPath 230 | if eventPath == "" { 231 | return errGithubEventPathEmpty 232 | } 233 | 234 | ghClient := github.NewClient(context.Background()) 235 | if repoInfo, err := ghClient.ParseFromFile(eventPath); err == nil && 236 | o.parseFromRepoInfo(repoInfo) { 237 | return nil 238 | } 239 | 240 | if repoInfo, err := ghClient.ParseFromURL(o.GithubAPIURL, o.GithubRepository); err == nil && 241 | o.parseFromRepoInfo(repoInfo) { 242 | return nil 243 | } 244 | 245 | return errGitHubRepoInfoUnavailable 246 | } 247 | 248 | func (o *Options) parseFromRepoInfo(repoInfo github.RepoInfo) bool { 249 | if repoInfo.Repo.DefaultBranch == nil && 250 | repoInfo.Repo.Fork == nil && 251 | repoInfo.Repo.Private == nil { 252 | return false 253 | } 254 | if repoInfo.Repo.Private != nil { 255 | o.PrivateRepoStr = strconv.FormatBool(*repoInfo.Repo.Private) 256 | } 257 | if repoInfo.Repo.Fork != nil { 258 | o.IsForkStr = strconv.FormatBool(*repoInfo.Repo.Fork) 259 | } 260 | if repoInfo.Repo.DefaultBranch != nil { 261 | o.DefaultBranch = *repoInfo.Repo.DefaultBranch 262 | } 263 | return true 264 | } 265 | 266 | func (o *Options) isPullRequestEvent() bool { 267 | return strings.HasPrefix(o.GithubEventName, pullRequestEvent) 268 | } 269 | 270 | func (o *Options) isDefaultBranch() bool { 271 | return o.GithubRef == fmt.Sprintf("refs/heads/%s", o.DefaultBranch) 272 | } 273 | -------------------------------------------------------------------------------- /options/testdata/bad-data.json: -------------------------------------------------------------------------------- 1 | ewogICJhZnRlciI6ICJhYTA0OTZhYTZlZDUxMDI2NDJmMzUyYTVjNGFkM2NmMDkwMDE3Yzc2IiwKICAiYmFzZV9yZWYiOiBudWxsLAogICJiZWZvcmUiOiAiM2Q0NzFmZDM2ZDdmMTE0Nzg0M2M2OWQ2OGRlMzVkMzIxZDM2ZmU0MyIsCiAgImNvbW1pdHMiOiBbCiAgICB7CiAgICAgICJhdXRob3IiOiB7CiAgICAgICAgImVtYWlsIjogIjY0NTA1MDk5K2xhdXJlbnRzaW1vbkB1c2Vycy5ub3JlcGx5LmdpdGh1Yi5jb20iLAogICAgICAgICJuYW1lIjogImxhdXJlbnRzaW1vbiIsCiAgICAgICAgInVzZXJuYW1lIjogImxhdXJlbnRzaW1vbiIKICAgICAgfSwKICAgICAgImNvbW1pdHRlciI6IHsKICAgICAgICAiZW1haWwiOiAibm9yZXBseUBnaXRodWIuY29tIiwKICAgICAgICAibmFtZSI6ICJHaXRIdWIiLAogICAgICAgICJ1c2VybmFtZSI6ICJ3ZWItZmxvdyIKICAgICAgfSwKICAgICAgImRpc3RpbmN0IjogdHJ1ZSwKICAgICAgImlkIjogImFhMDQ5NmFhNmVkNTEwMjY0MmYzNTJhNWM0YWQzY2YwOTAwMTdjNzYiLAogICAgICAibWVzc2FnZSI6ICJVcGRhdGUgZHVtbXkiLAogICAgICAidGltZXN0YW1wIjogIjIwMjItMDEtMTBUMTQ6MjQ6NDQtMDg6MDAiLAogICAgICAidHJlZV9pZCI6ICI5MTY3M2RlM2Y3OTg0ZDExMjc3ZGEzNDBkMjRiMDE4NzUyM2JkMjgzIiwKICAgICAgInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2NvbW1pdC9hYTA0OTZhYTZlZDUxMDI2NDJmMzUyYTVjNGFkM2NmMDkwMDE3Yzc2IgogICAgfQogIF0sCiAgImNvbXBhcmUiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9jb21wYXJlLzNkNDcxZmQzNmQ3Zi4uLmFhMDQ5NmFhNmVkNSIsCiAgImNyZWF0ZWQiOiBmYWxzZSwKICAiZGVsZXRlZCI6IGZhbHNlLAogICJmb3JjZWQiOiBmYWxzZSwKICAiaGVhZF9jb21taXQiOiB7CiAgICAiYXV0aG9yIjogewogICAgICAiZW1haWwiOiAiNjQ1MDUwOTkrbGF1cmVudHNpbW9uQHVzZXJzLm5vcmVwbHkuZ2l0aHViLmNvbSIsCiAgICAgICJuYW1lIjogImxhdXJlbnRzaW1vbiIsCiAgICAgICJ1c2VybmFtZSI6ICJsYXVyZW50c2ltb24iCiAgICB9LAogICAgImNvbW1pdHRlciI6IHsKICAgICAgImVtYWlsIjogIm5vcmVwbHlAZ2l0aHViLmNvbSIsCiAgICAgICJuYW1lIjogIkdpdEh1YiIsCiAgICAgICJ1c2VybmFtZSI6ICJ3ZWItZmxvdyIKICAgIH0sCiAgICAiZGlzdGluY3QiOiB0cnVlLAogICAgImlkIjogImFhMDQ5NmFhNmVkNTEwMjY0MmYzNTJhNWM0YWQzY2YwOTAwMTdjNzYiLAogICAgIm1lc3NhZ2UiOiAiVXBkYXRlIGR1bW15IiwKICAgICJ0aW1lc3RhbXAiOiAiMjAyMi0wMS0xMFQxNDoyNDo0NC0wODowMCIsCiAgICAidHJlZV9pZCI6ICI5MTY3M2RlM2Y3OTg0ZDExMjc3ZGEzNDBkMjRiMDE4NzUyM2JkMjgzIiwKICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9jb21taXQvYWEwNDk2YWE2ZWQ1MTAyNjQyZjM1MmE1YzRhZDNjZjA5MDAxN2M3NiIKICB9LAogICJwdXNoZXIiOiB7CiAgICAiZW1haWwiOiAiNjQ1MDUwOTkrbGF1cmVudHNpbW9uQHVzZXJzLm5vcmVwbHkuZ2l0aHViLmNvbSIsCiAgICAibmFtZSI6ICJsYXVyZW50c2ltb24iCiAgfSwKICAicmVmIjogInJlZnMvaGVhZHMvbWFpbiIsCiAgInJlcG9zaXRvcnkiOiB7CiAgICAiYWxsb3dfZm9ya2luZyI6IHRydWUsCiAgICAiYXJjaGl2ZV91cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIve2FyY2hpdmVfZm9ybWF0fXsvcmVmfSIsCiAgICAiYXJjaGl2ZWQiOiBmYWxzZSwKICAgICJhc3NpZ25lZXNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2Fzc2lnbmVlc3svdXNlcn0iLAogICAgImJsb2JzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9naXQvYmxvYnN7L3NoYX0iLAogICAgImJyYW5jaGVzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9icmFuY2hlc3svYnJhbmNofSIsCiAgICAiY2xvbmVfdXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbS9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIuZ2l0IiwKICAgICJjb2xsYWJvcmF0b3JzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9jb2xsYWJvcmF0b3Jzey9jb2xsYWJvcmF0b3J9IiwKICAgICJjb21tZW50c191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvY29tbWVudHN7L251bWJlcn0iLAogICAgImNvbW1pdHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2NvbW1pdHN7L3NoYX0iLAogICAgImNvbXBhcmVfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2NvbXBhcmUve2Jhc2V9Li4ue2hlYWR9IiwKICAgICJjb250ZW50c191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvY29udGVudHMveytwYXRofSIsCiAgICAiY29udHJpYnV0b3JzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9jb250cmlidXRvcnMiLAogICAgImNyZWF0ZWRfYXQiOiAxNjM2MTM3NDQ3LAogICAgImRlZmF1bHRfYnJhbmNoIjogIm1haW4iLAogICAgImRlcGxveW1lbnRzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9kZXBsb3ltZW50cyIsCiAgICAiZGVzY3JpcHRpb24iOiBudWxsLAogICAgImRpc2FibGVkIjogZmFsc2UsCiAgICAiZG93bmxvYWRzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9kb3dubG9hZHMiLAogICAgImV2ZW50c191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvZXZlbnRzIiwKICAgICJmb3JrcyI6IDAsCiAgICAiZm9ya3NfY291bnQiOiAwLAogICAgImZvcmtzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9mb3JrcyIsCiAgICAiZnVsbF9uYW1lIjogImxhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMiIsCiAgICAiZ2l0X2NvbW1pdHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2dpdC9jb21taXRzey9zaGF9IiwKICAgICJnaXRfcmVmc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvZ2l0L3JlZnN7L3NoYX0iLAogICAgImdpdF90YWdzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9naXQvdGFnc3svc2hhfSIsCiAgICAiZ2l0X3VybCI6ICJnaXQ6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi5naXQiLAogICAgImhhc19kb3dubG9hZHMiOiB0cnVlLAogICAgImhhc19pc3N1ZXMiOiB0cnVlLAogICAgImhhc19wYWdlcyI6IGZhbHNlLAogICAgImhhc19wcm9qZWN0cyI6IHRydWUsCiAgICAiaGFzX3dpa2kiOiB0cnVlLAogICAgImhvbWVwYWdlIjogbnVsbCwKICAgICJob29rc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvaG9va3MiLAogICAgImh0bWxfdXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbS9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIiLAogICAgImlkIjogNDI1MDQ5OTY2LAogICAgImlzX3RlbXBsYXRlIjogZmFsc2UsCiAgICAiaXNzdWVfY29tbWVudF91cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvaXNzdWVzL2NvbW1lbnRzey9udW1iZXJ9IiwKICAgICJpc3N1ZV9ldmVudHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2lzc3Vlcy9ldmVudHN7L251bWJlcn0iLAogICAgImlzc3Vlc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvaXNzdWVzey9udW1iZXJ9IiwKICAgICJrZXlzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9rZXlzey9rZXlfaWR9IiwKICAgICJsYWJlbHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2xhYmVsc3svbmFtZX0iLAogICAgImxhbmd1YWdlIjogbnVsbCwKICAgICJsYW5ndWFnZXNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL2xhbmd1YWdlcyIsCiAgICAibGljZW5zZSI6IG51bGwsCiAgICAibWFzdGVyX2JyYW5jaCI6ICJtYWluIiwKICAgICJtZXJnZXNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL21lcmdlcyIsCiAgICAibWlsZXN0b25lc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvbWlsZXN0b25lc3svbnVtYmVyfSIsCiAgICAibWlycm9yX3VybCI6IG51bGwsCiAgICAibmFtZSI6ICJzY29yZWNhcmQtYWN0aW9uLXRlc3QtMiIsCiAgICAibm9kZV9pZCI6ICJSX2tnRE9HVldfYmciLAogICAgIm5vdGlmaWNhdGlvbnNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL25vdGlmaWNhdGlvbnN7P3NpbmNlLGFsbCxwYXJ0aWNpcGF0aW5nfSIsCiAgICAib3Blbl9pc3N1ZXMiOiAwLAogICAgIm9wZW5faXNzdWVzX2NvdW50IjogMCwKICAgICJvd25lciI6IHsKICAgICAgImF2YXRhcl91cmwiOiAiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzY0NTA1MDk5P3Y9NCIsCiAgICAgICJlbWFpbCI6ICI2NDUwNTA5OStsYXVyZW50c2ltb25AdXNlcnMubm9yZXBseS5naXRodWIuY29tIiwKICAgICAgImV2ZW50c191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vZXZlbnRzey9wcml2YWN5fSIsCiAgICAgICJmb2xsb3dlcnNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL2ZvbGxvd2VycyIsCiAgICAgICJmb2xsb3dpbmdfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL2ZvbGxvd2luZ3svb3RoZXJfdXNlcn0iLAogICAgICAiZ2lzdHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL2dpc3Rzey9naXN0X2lkfSIsCiAgICAgICJncmF2YXRhcl9pZCI6ICIiLAogICAgICAiaHRtbF91cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbiIsCiAgICAgICJpZCI6IDY0NTA1MDk5LAogICAgICAibG9naW4iOiAibGF1cmVudHNpbW9uIiwKICAgICAgIm5hbWUiOiAibGF1cmVudHNpbW9uIiwKICAgICAgIm5vZGVfaWQiOiAiTURRNlZYTmxjalkwTlRBMU1EazUiLAogICAgICAib3JnYW5pemF0aW9uc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vb3JncyIsCiAgICAgICJyZWNlaXZlZF9ldmVudHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL3JlY2VpdmVkX2V2ZW50cyIsCiAgICAgICJyZXBvc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vcmVwb3MiLAogICAgICAic2l0ZV9hZG1pbiI6IGZhbHNlLAogICAgICAic3RhcnJlZF91cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vc3RhcnJlZHsvb3duZXJ9ey9yZXBvfSIsCiAgICAgICJzdWJzY3JpcHRpb25zX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2xhdXJlbnRzaW1vbi9zdWJzY3JpcHRpb25zIiwKICAgICAgInR5cGUiOiAiVXNlciIsCiAgICAgICJ1cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24iCiAgICB9LAogICAgInByaXZhdGUiOiB0cnVlLAogICAgInB1bGxzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9wdWxsc3svbnVtYmVyfSIsCiAgICAicHVzaGVkX2F0IjogMTY0MTg1MzQ4NCwKICAgICJyZWxlYXNlc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvcmVsZWFzZXN7L2lkfSIsCiAgICAic2l6ZSI6IDMxNSwKICAgICJzc2hfdXJsIjogImdpdEBnaXRodWIuY29tOmxhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi5naXQiLAogICAgInN0YXJnYXplcnMiOiAwLAogICAgInN0YXJnYXplcnNfY291bnQiOiAwLAogICAgInN0YXJnYXplcnNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL3N0YXJnYXplcnMiLAogICAgInN0YXR1c2VzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9zdGF0dXNlcy97c2hhfSIsCiAgICAic3Vic2NyaWJlcnNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL3N1YnNjcmliZXJzIiwKICAgICJzdWJzY3JpcHRpb25fdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3MvbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yL3N1YnNjcmlwdGlvbiIsCiAgICAic3ZuX3VybCI6ICJodHRwczovL2dpdGh1Yi5jb20vbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yIiwKICAgICJ0YWdzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi90YWdzIiwKICAgICJ0ZWFtc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9sYXVyZW50c2ltb24vc2NvcmVjYXJkLWFjdGlvbi10ZXN0LTIvdGVhbXMiLAogICAgInRvcGljcyI6IFtdLAogICAgInRyZWVzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zL2xhdXJlbnRzaW1vbi9zY29yZWNhcmQtYWN0aW9uLXRlc3QtMi9naXQvdHJlZXN7L3NoYX0iLAogICAgInVwZGF0ZWRfYXQiOiAiMjAyMi0wMS0xMFQyMjoxNjowMVoiLAogICAgInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vbGF1cmVudHNpbW9uL3Njb3JlY2FyZC1hY3Rpb24tdGVzdC0yIiwKICAgICJ2aXNpYmlsaXR5IjogInByaXZhdGUiLAogICAgIndhdGNoZXJzIjogMCwKICAgICJ3YXRjaGVyc19jb3VudCI6IDAKICB9LAogICJzZW5kZXIiOiB7CiAgICAiYXZhdGFyX3VybCI6ICJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvNjQ1MDUwOTk/dj00IiwKICAgICJldmVudHNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL2V2ZW50c3svcHJpdmFjeX0iLAogICAgImZvbGxvd2Vyc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vZm9sbG93ZXJzIiwKICAgICJmb2xsb3dpbmdfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL2ZvbGxvd2luZ3svb3RoZXJfdXNlcn0iLAogICAgImdpc3RzX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2xhdXJlbnRzaW1vbi9naXN0c3svZ2lzdF9pZH0iLAogICAgImdyYXZhdGFyX2lkIjogIiIsCiAgICAiaHRtbF91cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbiIsCiAgICAiaWQiOiA2NDUwNTA5OSwKICAgICJsb2dpbiI6ICJsYXVyZW50c2ltb24iLAogICAgIm5vZGVfaWQiOiAiTURRNlZYTmxjalkwTlRBMU1EazUiLAogICAgIm9yZ2FuaXphdGlvbnNfdXJsIjogImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbGF1cmVudHNpbW9uL29yZ3MiLAogICAgInJlY2VpdmVkX2V2ZW50c191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vcmVjZWl2ZWRfZXZlbnRzIiwKICAgICJyZXBvc191cmwiOiAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9sYXVyZW50c2ltb24vcmVwb3MiLAogICAgInNpdGVfYWRtaW4iOiBmYWxzZSwKICAgICJzdGFycmVkX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2xhdXJlbnRzaW1vbi9zdGFycmVkey9vd25lcn17L3JlcG99IiwKICAgICJzdWJzY3JpcHRpb25zX3VybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2xhdXJlbnRzaW1vbi9zdWJzY3JpcHRpb25zIiwKICAgICJ0eXBlIjogIlVzZXIiLAogICAgInVybCI6ICJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL2xhdXJlbnRzaW1vbiIKICB9Cn0K 2 | -------------------------------------------------------------------------------- /options/testdata/fork.json: -------------------------------------------------------------------------------- 1 | { 2 | "after": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 3 | "base_ref": null, 4 | "before": "3d471fd36d7f1147843c69d68de35d321d36fe43", 5 | "commits": [ 6 | { 7 | "author": { 8 | "email": "64505099+laurentsimon@users.noreply.github.com", 9 | "name": "laurentsimon", 10 | "username": "laurentsimon" 11 | }, 12 | "committer": { 13 | "email": "noreply@github.com", 14 | "name": "GitHub", 15 | "username": "web-flow" 16 | }, 17 | "distinct": true, 18 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 19 | "message": "Update dummy", 20 | "timestamp": "2022-01-10T14:24:44-08:00", 21 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 22 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 23 | } 24 | ], 25 | "compare": "https://github.com/laurentsimon/scorecard-action-test-2/compare/3d471fd36d7f...aa0496aa6ed5", 26 | "created": false, 27 | "deleted": false, 28 | "forced": false, 29 | "head_commit": { 30 | "author": { 31 | "email": "64505099+laurentsimon@users.noreply.github.com", 32 | "name": "laurentsimon", 33 | "username": "laurentsimon" 34 | }, 35 | "committer": { 36 | "email": "noreply@github.com", 37 | "name": "GitHub", 38 | "username": "web-flow" 39 | }, 40 | "distinct": true, 41 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 42 | "message": "Update dummy", 43 | "timestamp": "2022-01-10T14:24:44-08:00", 44 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 45 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 46 | }, 47 | "pusher": { 48 | "email": "64505099+laurentsimon@users.noreply.github.com", 49 | "name": "laurentsimon" 50 | }, 51 | "ref": "refs/heads/main", 52 | "repository": { 53 | "allow_forking": true, 54 | "archive_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/{archive_format}{/ref}", 55 | "archived": false, 56 | "assignees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/assignees{/user}", 57 | "blobs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/blobs{/sha}", 58 | "branches_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/branches{/branch}", 59 | "clone_url": "https://github.com/laurentsimon/scorecard-action-test-2.git", 60 | "collaborators_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/collaborators{/collaborator}", 61 | "comments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/comments{/number}", 62 | "commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/commits{/sha}", 63 | "compare_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/compare/{base}...{head}", 64 | "contents_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contents/{+path}", 65 | "contributors_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contributors", 66 | "created_at": 1636137447, 67 | "default_branch": "main", 68 | "deployments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/deployments", 69 | "description": null, 70 | "disabled": false, 71 | "downloads_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/downloads", 72 | "events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/events", 73 | "fork": true, 74 | "forks": 0, 75 | "forks_count": 0, 76 | "forks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/forks", 77 | "full_name": "laurentsimon/scorecard-action-test-2", 78 | "git_commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/commits{/sha}", 79 | "git_refs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/refs{/sha}", 80 | "git_tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/tags{/sha}", 81 | "git_url": "git://github.com/laurentsimon/scorecard-action-test-2.git", 82 | "has_downloads": true, 83 | "has_issues": true, 84 | "has_pages": false, 85 | "has_projects": true, 86 | "has_wiki": true, 87 | "homepage": null, 88 | "hooks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/hooks", 89 | "html_url": "https://github.com/laurentsimon/scorecard-action-test-2", 90 | "id": 425049966, 91 | "is_template": false, 92 | "issue_comment_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/comments{/number}", 93 | "issue_events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/events{/number}", 94 | "issues_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues{/number}", 95 | "keys_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/keys{/key_id}", 96 | "labels_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/labels{/name}", 97 | "language": null, 98 | "languages_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/languages", 99 | "license": null, 100 | "master_branch": "main", 101 | "merges_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/merges", 102 | "milestones_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/milestones{/number}", 103 | "mirror_url": null, 104 | "name": "scorecard-action-test-2", 105 | "node_id": "R_kgDOGVW_bg", 106 | "notifications_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/notifications{?since,all,participating}", 107 | "open_issues": 0, 108 | "open_issues_count": 0, 109 | "owner": { 110 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 111 | "email": "64505099+laurentsimon@users.noreply.github.com", 112 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 113 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 114 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 115 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 116 | "gravatar_id": "", 117 | "html_url": "https://github.com/laurentsimon", 118 | "id": 64505099, 119 | "login": "laurentsimon", 120 | "name": "laurentsimon", 121 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 122 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 123 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 124 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 125 | "site_admin": false, 126 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 127 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 128 | "type": "User", 129 | "url": "https://api.github.com/users/laurentsimon" 130 | }, 131 | "private": true, 132 | "pulls_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/pulls{/number}", 133 | "pushed_at": 1641853484, 134 | "releases_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/releases{/id}", 135 | "size": 315, 136 | "ssh_url": "git@github.com:laurentsimon/scorecard-action-test-2.git", 137 | "stargazers": 0, 138 | "stargazers_count": 0, 139 | "stargazers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/stargazers", 140 | "statuses_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/statuses/{sha}", 141 | "subscribers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscribers", 142 | "subscription_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscription", 143 | "svn_url": "https://github.com/laurentsimon/scorecard-action-test-2", 144 | "tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/tags", 145 | "teams_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/teams", 146 | "topics": [], 147 | "trees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/trees{/sha}", 148 | "updated_at": "2022-01-10T22:16:01Z", 149 | "url": "https://github.com/laurentsimon/scorecard-action-test-2", 150 | "visibility": "private", 151 | "watchers": 0, 152 | "watchers_count": 0 153 | }, 154 | "sender": { 155 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 156 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 157 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 158 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 159 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 160 | "gravatar_id": "", 161 | "html_url": "https://github.com/laurentsimon", 162 | "id": 64505099, 163 | "login": "laurentsimon", 164 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 165 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 166 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 167 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 168 | "site_admin": false, 169 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 170 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 171 | "type": "User", 172 | "url": "https://api.github.com/users/laurentsimon" 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /options/testdata/incorrect.json: -------------------------------------------------------------------------------- 1 | { 2 | "after": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 3 | "base_ref": null, 4 | "before": "3d471fd36d7f1147843c69d68de35d321d36fe43", 5 | "commits": [ 6 | { 7 | "author": { 8 | "email": "64505099+laurentsimon@users.noreply.github.com", 9 | "name": "laurentsimon", 10 | "username": "laurentsimon" 11 | }, 12 | "committer": { 13 | "email": "noreply@github.com", 14 | "name": "GitHub", 15 | "username": "web-flow" 16 | }, 17 | "distinct": true, 18 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 19 | "message": "Update dummy", 20 | "timestamp": "2022-01-10T14:24:44-08:00", 21 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 22 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 23 | } 24 | ], 25 | "compare": "https://github.com/laurentsimon/scorecard-action-test-2/compare/3d471fd36d7f...aa0496aa6ed5", 26 | "created": false, 27 | "deleted": false, 28 | "forced": false, 29 | "head_commit": { 30 | "author": { 31 | "email": "64505099+laurentsimon@users.noreply.github.com", 32 | "name": "laurentsimon", 33 | "username": "laurentsimon" 34 | }, 35 | "committer": { 36 | "email": "noreply@github.com", 37 | "name": "GitHub", 38 | "username": "web-flow" 39 | }, 40 | "distinct": true, 41 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 42 | "message": "Update dummy", 43 | "timestamp": "2022-01-10T14:24:44-08:00", 44 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 45 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 46 | }, 47 | "pusher": { 48 | "email": "64505099+laurentsimon@users.noreply.github.com", 49 | "name": "laurentsimon" 50 | }, 51 | "ref": "refs/heads/main", 52 | "repository": { 53 | "allow_forking": true, 54 | "archive_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/{archive_format}{/ref}", 55 | "archived": false, 56 | "assignees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/assignees{/user}", 57 | "blobs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/blobs{/sha}", 58 | "branches_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/branches{/branch}", 59 | "clone_url": "https://github.com/laurentsimon/scorecard-action-test-2.git", 60 | "collaborators_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/collaborators{/collaborator}", 61 | "comments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/comments{/number}", 62 | "commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/commits{/sha}", 63 | "compare_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/compare/{base}...{head}", 64 | "contents_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contents/{+path}", 65 | "contributors_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contributors", 66 | "created_at": 1636137447, 67 | "default_branch": "main", 68 | "deployments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/deployments", 69 | "description": null, 70 | "disabled": false, 71 | "downloads_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/downloads", 72 | "events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/events", 73 | "fork": true, 74 | "forks": 0, 75 | "forks_count": 0, 76 | "forks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/forks", 77 | "full_name": "laurentsimon/scorecard-action-test-2", 78 | "git_commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/commits{/sha}", 79 | "git_refs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/refs{/sha}", 80 | "git_tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/tags{/sha}", 81 | "git_url": "git://github.com/laurentsimon/scorecard-action-test-2.git", 82 | "has_downloads": true, 83 | "has_issues": true, 84 | "has_pages": false, 85 | "has_projects": true, 86 | "has_wiki": true, 87 | "homepage": null, 88 | "hooks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/hooks", 89 | "html_url": "https://github.com/laurentsimon/scorecard-action-test-2", 90 | "id": 425049966, 91 | "is_template": false, 92 | "issue_comment_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/comments{/number}", 93 | "issue_events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/events{/number}", 94 | "issues_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues{/number}", 95 | "keys_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/keys{/key_id}", 96 | "labels_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/labels{/name}", 97 | "language": null, 98 | "languages_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/languages", 99 | "license": null, 100 | "master_branch": "main", 101 | "merges_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/merges", 102 | "milestones_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/milestones{/number}", 103 | "mirror_url": null, 104 | "name": "scorecard-action-test-2", 105 | "node_id": "R_kgDOGVW_bg", 106 | "notifications_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/notifications{?since,all,participating}", 107 | "open_issues": 0, 108 | "open_issues_count": 0, 109 | "owner": { 110 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 111 | "email": "64505099+laurentsimon@users.noreply.github.com", 112 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 113 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 114 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 115 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 116 | "gravatar_id": "", 117 | "html_url": "https://github.com/laurentsimon", 118 | "id": 64505099, 119 | "login": "laurentsimon", 120 | "name": "laurentsimon", 121 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 122 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 123 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 124 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 125 | "site_admin": false, 126 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 127 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 128 | "type": "User", 129 | "url": "https://api.github.com/users/laurentsimon" 130 | }, 131 | "private": true, 132 | "pulls_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/pulls{/number}", 133 | "pushed_at": 1641853484, 134 | "releases_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/releases{/id}", 135 | "size": 315, 136 | "ssh_url": "git@github.com:laurentsimon/scorecard-action-test-2.git", 137 | "stargazers": 0, 138 | "stargazers_count": 0, 139 | "stargazers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/stargazers", 140 | "statuses_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/statuses/{sha}", 141 | "subscribers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscribers", 142 | "subscription_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscription", 143 | "svn_url": "https://github.com/laurentsimon/scorecard-action-test-2", 144 | "tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/tags", 145 | "teams_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/teams", 146 | "topics": [], 147 | "trees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/trees{/sha}", 148 | "updated_at": "2022-01-10T22:16:01Z", 149 | "url": "https://github.com/laurentsimon/scorecard-action-test-2", 150 | "visibility": "private", 151 | "watchers": 0, 152 | "watchers_count": 0 153 | }, 154 | "sender": { 155 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 156 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 157 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 158 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 159 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 160 | "gravatar_id": "", 161 | "html_url": "https://github.com/laurentsimon", 162 | "id": 64505099, 163 | "login": "laurentsimon", 164 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 165 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 166 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 167 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 168 | "site_admin": false, 169 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 170 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 171 | "type": "User", 172 | "url": "https://api.github.com/users/laurentsimon" 173 | } 174 | 175 | -------------------------------------------------------------------------------- /options/testdata/non-fork.json: -------------------------------------------------------------------------------- 1 | { 2 | "after": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 3 | "base_ref": null, 4 | "before": "3d471fd36d7f1147843c69d68de35d321d36fe43", 5 | "commits": [ 6 | { 7 | "author": { 8 | "email": "64505099+laurentsimon@users.noreply.github.com", 9 | "name": "laurentsimon", 10 | "username": "laurentsimon" 11 | }, 12 | "committer": { 13 | "email": "noreply@github.com", 14 | "name": "GitHub", 15 | "username": "web-flow" 16 | }, 17 | "distinct": true, 18 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 19 | "message": "Update dummy", 20 | "timestamp": "2022-01-10T14:24:44-08:00", 21 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 22 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 23 | } 24 | ], 25 | "compare": "https://github.com/laurentsimon/scorecard-action-test-2/compare/3d471fd36d7f...aa0496aa6ed5", 26 | "created": false, 27 | "deleted": false, 28 | "forced": false, 29 | "head_commit": { 30 | "author": { 31 | "email": "64505099+laurentsimon@users.noreply.github.com", 32 | "name": "laurentsimon", 33 | "username": "laurentsimon" 34 | }, 35 | "committer": { 36 | "email": "noreply@github.com", 37 | "name": "GitHub", 38 | "username": "web-flow" 39 | }, 40 | "distinct": true, 41 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 42 | "message": "Update dummy", 43 | "timestamp": "2022-01-10T14:24:44-08:00", 44 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 45 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 46 | }, 47 | "pusher": { 48 | "email": "64505099+laurentsimon@users.noreply.github.com", 49 | "name": "laurentsimon" 50 | }, 51 | "ref": "refs/heads/main", 52 | "repository": { 53 | "allow_forking": true, 54 | "archive_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/{archive_format}{/ref}", 55 | "archived": false, 56 | "assignees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/assignees{/user}", 57 | "blobs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/blobs{/sha}", 58 | "branches_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/branches{/branch}", 59 | "clone_url": "https://github.com/laurentsimon/scorecard-action-test-2.git", 60 | "collaborators_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/collaborators{/collaborator}", 61 | "comments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/comments{/number}", 62 | "commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/commits{/sha}", 63 | "compare_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/compare/{base}...{head}", 64 | "contents_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contents/{+path}", 65 | "contributors_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contributors", 66 | "created_at": 1636137447, 67 | "default_branch": "main", 68 | "deployments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/deployments", 69 | "description": null, 70 | "disabled": false, 71 | "downloads_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/downloads", 72 | "events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/events", 73 | "forks": 0, 74 | "forks_count": 0, 75 | "forks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/forks", 76 | "full_name": "laurentsimon/scorecard-action-test-2", 77 | "git_commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/commits{/sha}", 78 | "git_refs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/refs{/sha}", 79 | "git_tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/tags{/sha}", 80 | "git_url": "git://github.com/laurentsimon/scorecard-action-test-2.git", 81 | "has_downloads": true, 82 | "has_issues": true, 83 | "has_pages": false, 84 | "has_projects": true, 85 | "has_wiki": true, 86 | "homepage": null, 87 | "hooks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/hooks", 88 | "html_url": "https://github.com/laurentsimon/scorecard-action-test-2", 89 | "id": 425049966, 90 | "is_template": false, 91 | "issue_comment_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/comments{/number}", 92 | "issue_events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/events{/number}", 93 | "issues_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues{/number}", 94 | "keys_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/keys{/key_id}", 95 | "labels_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/labels{/name}", 96 | "language": null, 97 | "languages_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/languages", 98 | "license": null, 99 | "master_branch": "main", 100 | "merges_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/merges", 101 | "milestones_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/milestones{/number}", 102 | "mirror_url": null, 103 | "name": "scorecard-action-test-2", 104 | "node_id": "R_kgDOGVW_bg", 105 | "notifications_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/notifications{?since,all,participating}", 106 | "open_issues": 0, 107 | "open_issues_count": 0, 108 | "owner": { 109 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 110 | "email": "64505099+laurentsimon@users.noreply.github.com", 111 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 112 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 113 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 114 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 115 | "gravatar_id": "", 116 | "html_url": "https://github.com/laurentsimon", 117 | "id": 64505099, 118 | "login": "laurentsimon", 119 | "name": "laurentsimon", 120 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 121 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 122 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 123 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 124 | "site_admin": false, 125 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 126 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 127 | "type": "User", 128 | "url": "https://api.github.com/users/laurentsimon" 129 | }, 130 | "private": true, 131 | "pulls_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/pulls{/number}", 132 | "pushed_at": 1641853484, 133 | "releases_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/releases{/id}", 134 | "size": 315, 135 | "ssh_url": "git@github.com:laurentsimon/scorecard-action-test-2.git", 136 | "stargazers": 0, 137 | "stargazers_count": 0, 138 | "stargazers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/stargazers", 139 | "statuses_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/statuses/{sha}", 140 | "subscribers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscribers", 141 | "subscription_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscription", 142 | "svn_url": "https://github.com/laurentsimon/scorecard-action-test-2", 143 | "tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/tags", 144 | "teams_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/teams", 145 | "topics": [], 146 | "trees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/trees{/sha}", 147 | "updated_at": "2022-01-10T22:16:01Z", 148 | "url": "https://github.com/laurentsimon/scorecard-action-test-2", 149 | "visibility": "private", 150 | "watchers": 0, 151 | "watchers_count": 0 152 | }, 153 | "sender": { 154 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 155 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 156 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 157 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 158 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 159 | "gravatar_id": "", 160 | "html_url": "https://github.com/laurentsimon", 161 | "id": 64505099, 162 | "login": "laurentsimon", 163 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 164 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 165 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 166 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 167 | "site_admin": false, 168 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 169 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 170 | "type": "User", 171 | "url": "https://api.github.com/users/laurentsimon" 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /options/testdata/public.json: -------------------------------------------------------------------------------- 1 | { 2 | "after": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 3 | "base_ref": null, 4 | "before": "3d471fd36d7f1147843c69d68de35d321d36fe43", 5 | "commits": [ 6 | { 7 | "author": { 8 | "email": "64505099+laurentsimon@users.noreply.github.com", 9 | "name": "laurentsimon", 10 | "username": "laurentsimon" 11 | }, 12 | "committer": { 13 | "email": "noreply@github.com", 14 | "name": "GitHub", 15 | "username": "web-flow" 16 | }, 17 | "distinct": true, 18 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 19 | "message": "Update dummy", 20 | "timestamp": "2022-01-10T14:24:44-08:00", 21 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 22 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 23 | } 24 | ], 25 | "compare": "https://github.com/laurentsimon/scorecard-action-test-2/compare/3d471fd36d7f...aa0496aa6ed5", 26 | "created": false, 27 | "deleted": false, 28 | "forced": false, 29 | "head_commit": { 30 | "author": { 31 | "email": "64505099+laurentsimon@users.noreply.github.com", 32 | "name": "laurentsimon", 33 | "username": "laurentsimon" 34 | }, 35 | "committer": { 36 | "email": "noreply@github.com", 37 | "name": "GitHub", 38 | "username": "web-flow" 39 | }, 40 | "distinct": true, 41 | "id": "aa0496aa6ed5102642f352a5c4ad3cf090017c76", 42 | "message": "Update dummy", 43 | "timestamp": "2022-01-10T14:24:44-08:00", 44 | "tree_id": "91673de3f7984d11277da340d24b0187523bd283", 45 | "url": "https://github.com/laurentsimon/scorecard-action-test-2/commit/aa0496aa6ed5102642f352a5c4ad3cf090017c76" 46 | }, 47 | "pusher": { 48 | "email": "64505099+laurentsimon@users.noreply.github.com", 49 | "name": "laurentsimon" 50 | }, 51 | "ref": "refs/heads/main", 52 | "repository": { 53 | "allow_forking": true, 54 | "archive_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/{archive_format}{/ref}", 55 | "archived": false, 56 | "assignees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/assignees{/user}", 57 | "blobs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/blobs{/sha}", 58 | "branches_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/branches{/branch}", 59 | "clone_url": "https://github.com/laurentsimon/scorecard-action-test-2.git", 60 | "collaborators_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/collaborators{/collaborator}", 61 | "comments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/comments{/number}", 62 | "commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/commits{/sha}", 63 | "compare_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/compare/{base}...{head}", 64 | "contents_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contents/{+path}", 65 | "contributors_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/contributors", 66 | "created_at": 1636137447, 67 | "default_branch": "main", 68 | "deployments_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/deployments", 69 | "description": null, 70 | "disabled": false, 71 | "downloads_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/downloads", 72 | "events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/events", 73 | "forks": 0, 74 | "forks_count": 0, 75 | "forks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/forks", 76 | "full_name": "laurentsimon/scorecard-action-test-2", 77 | "git_commits_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/commits{/sha}", 78 | "git_refs_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/refs{/sha}", 79 | "git_tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/tags{/sha}", 80 | "git_url": "git://github.com/laurentsimon/scorecard-action-test-2.git", 81 | "has_downloads": true, 82 | "has_issues": true, 83 | "has_pages": false, 84 | "has_projects": true, 85 | "has_wiki": true, 86 | "homepage": null, 87 | "hooks_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/hooks", 88 | "html_url": "https://github.com/laurentsimon/scorecard-action-test-2", 89 | "id": 425049966, 90 | "is_template": false, 91 | "issue_comment_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/comments{/number}", 92 | "issue_events_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues/events{/number}", 93 | "issues_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/issues{/number}", 94 | "keys_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/keys{/key_id}", 95 | "labels_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/labels{/name}", 96 | "language": null, 97 | "languages_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/languages", 98 | "license": null, 99 | "master_branch": "main", 100 | "merges_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/merges", 101 | "milestones_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/milestones{/number}", 102 | "mirror_url": null, 103 | "name": "scorecard-action-test-2", 104 | "node_id": "R_kgDOGVW_bg", 105 | "notifications_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/notifications{?since,all,participating}", 106 | "open_issues": 0, 107 | "open_issues_count": 0, 108 | "owner": { 109 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 110 | "email": "64505099+laurentsimon@users.noreply.github.com", 111 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 112 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 113 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 114 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 115 | "gravatar_id": "", 116 | "html_url": "https://github.com/laurentsimon", 117 | "id": 64505099, 118 | "login": "laurentsimon", 119 | "name": "laurentsimon", 120 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 121 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 122 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 123 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 124 | "site_admin": false, 125 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 126 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 127 | "type": "User", 128 | "url": "https://api.github.com/users/laurentsimon" 129 | }, 130 | "private": false, 131 | "pulls_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/pulls{/number}", 132 | "pushed_at": 1641853484, 133 | "releases_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/releases{/id}", 134 | "size": 315, 135 | "ssh_url": "git@github.com:laurentsimon/scorecard-action-test-2.git", 136 | "stargazers": 0, 137 | "stargazers_count": 0, 138 | "stargazers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/stargazers", 139 | "statuses_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/statuses/{sha}", 140 | "subscribers_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscribers", 141 | "subscription_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/subscription", 142 | "svn_url": "https://github.com/laurentsimon/scorecard-action-test-2", 143 | "tags_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/tags", 144 | "teams_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/teams", 145 | "topics": [], 146 | "trees_url": "https://api.github.com/repos/laurentsimon/scorecard-action-test-2/git/trees{/sha}", 147 | "updated_at": "2022-01-10T22:16:01Z", 148 | "url": "https://github.com/laurentsimon/scorecard-action-test-2", 149 | "visibility": "private", 150 | "watchers": 0, 151 | "watchers_count": 0 152 | }, 153 | "sender": { 154 | "avatar_url": "https://avatars.githubusercontent.com/u/64505099?v=4", 155 | "events_url": "https://api.github.com/users/laurentsimon/events{/privacy}", 156 | "followers_url": "https://api.github.com/users/laurentsimon/followers", 157 | "following_url": "https://api.github.com/users/laurentsimon/following{/other_user}", 158 | "gists_url": "https://api.github.com/users/laurentsimon/gists{/gist_id}", 159 | "gravatar_id": "", 160 | "html_url": "https://github.com/laurentsimon", 161 | "id": 64505099, 162 | "login": "laurentsimon", 163 | "node_id": "MDQ6VXNlcjY0NTA1MDk5", 164 | "organizations_url": "https://api.github.com/users/laurentsimon/orgs", 165 | "received_events_url": "https://api.github.com/users/laurentsimon/received_events", 166 | "repos_url": "https://api.github.com/users/laurentsimon/repos", 167 | "site_admin": false, 168 | "starred_url": "https://api.github.com/users/laurentsimon/starred{/owner}{/repo}", 169 | "subscriptions_url": "https://api.github.com/users/laurentsimon/subscriptions", 170 | "type": "User", 171 | "url": "https://api.github.com/users/laurentsimon" 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /policies/template.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | version: 1 16 | policies: 17 | Token-Permissions: 18 | score: 10 19 | mode: enforced 20 | Branch-Protection: 21 | score: 10 22 | mode: enforced 23 | Code-Review: 24 | score: 10 25 | mode: enforced 26 | Dangerous-Workflow: 27 | score: 10 28 | mode: enforced 29 | License: 30 | score: 9 31 | mode: enforced 32 | Pinned-Dependencies: 33 | score: 10 34 | mode: enforced 35 | Security-Policy: 36 | score: 10 37 | mode: enforced 38 | SAST: 39 | score: 10 40 | mode: enforced 41 | Contributors: 42 | score: 10 43 | mode: disabled 44 | Packaging: 45 | score: 10 46 | mode: enforced 47 | Binary-Artifacts: 48 | score: 10 49 | mode: enforced 50 | Signed-Releases: 51 | score: 10 52 | mode: disabled 53 | Dependency-Update-Tool: 54 | score: 10 55 | mode: enforced 56 | Fuzzing: 57 | score: 10 58 | mode: enforced 59 | CII-Best-Practices: 60 | # passing score 61 | score: 5 62 | mode: enforced 63 | Vulnerabilities: 64 | score: 10 65 | mode: enforced 66 | CI-Tests: 67 | score: 10 68 | mode: enforced 69 | Maintained: 70 | score: 1 71 | mode: enforced -------------------------------------------------------------------------------- /signing/sign-random-data.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-action/05b42c624433fc40578a4040d5cf5e36ddca8cde/signing/sign-random-data.txt -------------------------------------------------------------------------------- /signing/signing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | // Package signing provides functionality to sign and upload results to the Scorecard API. 18 | package signing 19 | 20 | import ( 21 | "bytes" 22 | "context" 23 | "encoding/json" 24 | "errors" 25 | "fmt" 26 | "io" 27 | "log" 28 | "net/http" 29 | "net/url" 30 | "os" 31 | "strings" 32 | "time" 33 | 34 | sigOpts "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" 35 | "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" 36 | "github.com/sigstore/cosign/v2/pkg/cosign" 37 | 38 | "github.com/ossf/scorecard-action/options" 39 | ) 40 | 41 | var ( 42 | errorEmptyToken = errors.New("error token empty") 43 | errorInvalidToken = errors.New("invalid token") 44 | 45 | // backoff schedule for interactions with cosign/rekor and our web API. 46 | backoffSchedule = []time.Duration{ 47 | 1 * time.Second, 48 | 3 * time.Second, 49 | 10 * time.Second, 50 | } 51 | ) 52 | 53 | // Signing is a signing structure. 54 | type Signing struct { 55 | token string 56 | rekorTlogIndex int64 57 | } 58 | 59 | // New creates a new Signing instance. 60 | func New(token string) (*Signing, error) { 61 | // Set the default GITHUB_TOKEN, because it's not available by default 62 | // in a GitHub Action. We need it for OIDC. 63 | if token == "" { 64 | return nil, fmt.Errorf("%w", errorEmptyToken) 65 | } 66 | 67 | // Check for a workflow secret. 68 | if !strings.HasPrefix(token, "ghs_") { 69 | return nil, fmt.Errorf("%w: not a default GITHUB_TOKEN", errorInvalidToken) 70 | } 71 | if err := os.Setenv("GITHUB_TOKEN", token); err != nil { 72 | return nil, fmt.Errorf("error setting GITHUB_TOKEN env var: %w", err) 73 | } 74 | 75 | return &Signing{ 76 | token: token, 77 | }, nil 78 | } 79 | 80 | // SignScorecardResult signs the results file and uploads the attestation to the Rekor transparency log. 81 | func (s *Signing) SignScorecardResult(scorecardResultsFile string) error { 82 | f, err := os.CreateTemp("", "bundle") 83 | if err != nil { 84 | return fmt.Errorf("creating temporary bundle file: %w", err) 85 | } 86 | bundlePath := f.Name() 87 | f.Close() 88 | defer os.Remove(bundlePath) // clean up 89 | 90 | // Prepare settings for SignBlobCmd. 91 | rootOpts := &sigOpts.RootOptions{Timeout: sigOpts.DefaultTimeout} // Just the timeout. 92 | keyOpts := sigOpts.KeyOpts{ 93 | FulcioURL: sigOpts.DefaultFulcioURL, // Signing certificate provider. 94 | RekorURL: sigOpts.DefaultRekorURL, // Transparency log. 95 | OIDCIssuer: sigOpts.DefaultOIDCIssuerURL, // OIDC provider to get ID token to auth for Fulcio. 96 | OIDCClientID: "sigstore", 97 | SkipConfirmation: true, // skip cosign's privacy confirmation prompt as we run non-interactively 98 | BundlePath: bundlePath, 99 | } 100 | 101 | for _, backoff := range backoffSchedule { 102 | // This command will use the provided OIDCIssuer to authenticate into Fulcio, which will generate the 103 | // signing certificate on the scorecard result. This attestation is then uploaded to the Rekor transparency log. 104 | // The output bytes (signature) and certificate are discarded since verification can be done with just the payload. 105 | _, err = sign.SignBlobCmd(rootOpts, keyOpts, scorecardResultsFile, true, "", "", true) 106 | if err == nil { 107 | break 108 | } 109 | log.Printf("error signing scorecard results: %v\n", err) 110 | log.Printf("retrying in %v...\n", backoff) 111 | time.Sleep(backoff) 112 | } 113 | 114 | // retries failed 115 | if err != nil { 116 | return fmt.Errorf("error signing payload: %w", err) 117 | } 118 | 119 | rekorTlogIndex, err := extractTlogIndex(bundlePath) 120 | if err != nil { 121 | return err 122 | } 123 | s.rekorTlogIndex = rekorTlogIndex 124 | 125 | return nil 126 | } 127 | 128 | // ProcessSignature calls scorecard-api to process & upload signed scorecard results. 129 | func (s *Signing) ProcessSignature(jsonPayload []byte, repoName, repoRef string) error { 130 | // Prepare HTTP request body for scorecard-webapp-api call. 131 | // TODO: Use the `ScorecardResult` struct from `scorecard-webapp`. 132 | resultsPayload := struct { 133 | Result string `json:"result"` 134 | Branch string `json:"branch"` 135 | AccessToken string `json:"accessToken"` 136 | TlogIndex int64 `json:"tlogIndex"` 137 | }{ 138 | Result: string(jsonPayload), 139 | Branch: repoRef, 140 | AccessToken: s.token, 141 | TlogIndex: s.rekorTlogIndex, 142 | } 143 | 144 | payloadBytes, err := json.Marshal(resultsPayload) 145 | if err != nil { 146 | return fmt.Errorf("marshalling json results: %w", err) 147 | } 148 | 149 | apiURL := os.Getenv(options.EnvInputInternalPublishBaseURL) 150 | rawURL := fmt.Sprintf("%s/projects/github.com/%s", apiURL, repoName) 151 | postURL, err := url.Parse(rawURL) 152 | if err != nil { 153 | return fmt.Errorf("parsing Scorecard API endpoint: %w", err) 154 | } 155 | 156 | for _, backoff := range backoffSchedule { 157 | // Call scorecard-webapp-api to process and upload signature. 158 | err = postResults(postURL, payloadBytes) 159 | if err == nil { 160 | break 161 | } 162 | log.Printf("error sending scorecard results to webapp: %v\n", err) 163 | log.Printf("retrying in %v...\n", backoff) 164 | time.Sleep(backoff) 165 | } 166 | 167 | // retries failed 168 | if err != nil { 169 | return fmt.Errorf("error sending scorecard results to webapp: %w", err) 170 | } 171 | 172 | return nil 173 | } 174 | 175 | func postResults(endpoint *url.URL, payload []byte) error { 176 | req, err := http.NewRequest("POST", endpoint.String(), bytes.NewBuffer(payload)) 177 | if err != nil { 178 | return fmt.Errorf("creating HTTP request: %w", err) 179 | } 180 | req.Header.Set("Content-Type", "application/json") 181 | 182 | ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second) 183 | defer cancel() 184 | req = req.WithContext(ctx) 185 | 186 | // Execute request. 187 | client := &http.Client{} 188 | resp, err := client.Do(req) 189 | if err != nil { 190 | return fmt.Errorf("executing scorecard-api call: %w", err) 191 | } 192 | defer resp.Body.Close() 193 | 194 | if resp.StatusCode != http.StatusCreated { 195 | bodyBytes, err := io.ReadAll(resp.Body) 196 | if err != nil { 197 | return fmt.Errorf("reading response body: %w", err) 198 | } 199 | return fmt.Errorf("http response %d, status: %v, error: %v", resp.StatusCode, resp.Status, string(bodyBytes)) //nolint 200 | } 201 | 202 | return nil 203 | } 204 | 205 | func extractTlogIndex(bundlePath string) (int64, error) { 206 | contents, err := os.ReadFile(bundlePath) 207 | if err != nil { 208 | return 0, fmt.Errorf("reading cosign bundle file: %w", err) 209 | } 210 | var payload cosign.LocalSignedPayload 211 | err = json.Unmarshal(contents, &payload) 212 | if err != nil || payload.Bundle == nil { 213 | return 0, fmt.Errorf("invalid cosign bundle file: %w", err) 214 | } 215 | return payload.Bundle.Payload.LogIndex, nil 216 | } 217 | -------------------------------------------------------------------------------- /signing/signing_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 OpenSSF 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 | // SPDX-License-Identifier: Apache-2.0 16 | 17 | package signing 18 | 19 | import ( 20 | "net/http" 21 | "net/http/httptest" 22 | "os" 23 | "testing" 24 | "time" 25 | 26 | "github.com/ossf/scorecard-action/options" 27 | ) 28 | 29 | // TODO: For this test to work, fake the OIDC token retrieval with something like. 30 | //nolint // https://github.com/sigstore/cosign/blob/286bb0c58757009e99ab7080c720b30e51d08855/cmd/cosign/cli/fulcio/fulcio_test.go 31 | 32 | // func Test_SignScorecardResult(t *testing.T) { 33 | // t.Parallel() 34 | // // Generate random bytes to use as our payload. This is done because signing identical payloads twice 35 | // // just creates multiple entries under it, so we are keeping this test simple and not comparing timestamps. 36 | // fmt.Println("ACTIONS_ID_TOKEN_REQUEST_TOKEN:") 37 | // fmt.Println(os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN")) 38 | // scorecardResultsFile := "./sign-random-data.txt" 39 | // randomData := make([]byte, 20) 40 | // if _, err := rand.Read(randomData); err != nil { 41 | // t.Errorf("signScorecardResult() error generating random bytes, %v", err) 42 | // return 43 | // } 44 | // if err := ioutil.WriteFile(scorecardResultsFile, randomData, 0o600); err != nil { 45 | // t.Errorf("signScorecardResult() error writing random bytes to file, %v", err) 46 | // return 47 | // } 48 | 49 | // // Sign example scorecard results file. 50 | // err := SignScorecardResult(scorecardResultsFile) 51 | // if err != nil { 52 | // t.Errorf("signScorecardResult() error, %v", err) 53 | // return 54 | // } 55 | 56 | // // Verify that the signature was created and uploaded to the Rekor tlog by looking up the payload. 57 | // ctx := context.Background() 58 | // rekorClient, err := rekor.NewClient(options.DefaultRekorURL) 59 | // if err != nil { 60 | // t.Errorf("signScorecardResult() error getting Rekor client, %v", err) 61 | // return 62 | // } 63 | // scorecardResultData, err := ioutil.ReadFile(scorecardResultsFile) 64 | // if err != nil { 65 | // t.Errorf("signScorecardResult() error reading scorecard result file, %v", err) 66 | // return 67 | // } 68 | // uuids, err := cosign.FindTLogEntriesByPayload(ctx, rekorClient, scorecardResultData) 69 | // if err != nil { 70 | // t.Errorf("signScorecardResult() error getting tlog entries, %v", err) 71 | // return 72 | // } 73 | 74 | // if len(uuids) != 1 { 75 | // t.Errorf("signScorecardResult() error finding signature in Rekor tlog, %v", err) 76 | // return 77 | // } 78 | // } 79 | 80 | //nolint:paralleltest // we are using t.Setenv 81 | func TestProcessSignature(t *testing.T) { 82 | tests := []struct { 83 | name string 84 | payloadPath string 85 | status int 86 | wantErr bool 87 | }{ 88 | { 89 | name: "post succeeded", 90 | status: http.StatusCreated, 91 | payloadPath: "testdata/results.json", 92 | wantErr: false, 93 | }, 94 | { 95 | name: "post failed", 96 | status: http.StatusBadRequest, 97 | payloadPath: "testdata/results.json", 98 | wantErr: true, 99 | }, 100 | } 101 | // use smaller backoffs for the test so they run faster 102 | setBackoffs(t, []time.Duration{0, time.Millisecond, 2 * time.Millisecond}) 103 | for _, tt := range tests { 104 | t.Run(tt.name, func(t *testing.T) { 105 | jsonPayload, err := os.ReadFile(tt.payloadPath) 106 | if err != nil { 107 | t.Fatalf("Unexpected error reading testdata: %v", err) 108 | } 109 | repoName := "ossf-tests/scorecard-action" 110 | repoRef := "refs/heads/main" 111 | //nolint:gosec // dummy credentials 112 | accessToken := "ghs_foo" 113 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 114 | w.WriteHeader(tt.status) 115 | })) 116 | t.Setenv(options.EnvInputInternalPublishBaseURL, server.URL) 117 | t.Cleanup(server.Close) 118 | 119 | s, err := New(accessToken) 120 | if err != nil { 121 | t.Fatalf("Unexpected error New: %v", err) 122 | } 123 | err = s.ProcessSignature(jsonPayload, repoName, repoRef) 124 | if (err != nil) != tt.wantErr { 125 | t.Errorf("ProcessSignature() error: %v, wantErr: %v", err, tt.wantErr) 126 | } 127 | }) 128 | } 129 | } 130 | 131 | func Test_extractTlogIndex(t *testing.T) { 132 | t.Parallel() 133 | tests := []struct { 134 | name string 135 | bundlePath string 136 | want int64 137 | wantErr bool 138 | }{ 139 | { 140 | name: "valid bundle", 141 | bundlePath: "testdata/cosign.bundle", 142 | want: 23548006, 143 | }, 144 | { 145 | name: "invalid bundle", 146 | bundlePath: "testdata/invalid-cosign.bundle", 147 | wantErr: true, 148 | }, 149 | { 150 | name: "missing bundle", 151 | bundlePath: "testdata/does-not-exist.bundle", 152 | wantErr: true, 153 | }, 154 | } 155 | for _, tt := range tests { 156 | tt := tt 157 | t.Run(tt.name, func(t *testing.T) { 158 | t.Parallel() 159 | got, err := extractTlogIndex(tt.bundlePath) 160 | if (err != nil) != tt.wantErr { 161 | t.Fatalf("unexpected err: %v, wantErr: %t", err, tt.wantErr) 162 | } 163 | if got != tt.want { 164 | t.Errorf("wrong tlog index: got %d, wanted %d", got, tt.want) 165 | } 166 | }) 167 | } 168 | } 169 | 170 | //nolint:paralleltest // we are using t.Setenv 171 | func TestProcessSignature_retries(t *testing.T) { 172 | tests := []struct { 173 | name string 174 | nFailures int 175 | wantNRequests int 176 | wantErr bool 177 | }{ 178 | { 179 | name: "succeeds immediately", 180 | nFailures: 0, 181 | wantNRequests: 1, 182 | wantErr: false, 183 | }, 184 | { 185 | name: "one retry", 186 | nFailures: 1, 187 | wantNRequests: 2, 188 | wantErr: false, 189 | }, 190 | { 191 | // limit corresponds to backoffs set in test body 192 | name: "retry limit exceeded", 193 | nFailures: 4, 194 | wantNRequests: 3, 195 | wantErr: true, 196 | }, 197 | } 198 | // use smaller backoffs for the test so they run faster 199 | setBackoffs(t, []time.Duration{0, time.Millisecond, 2 * time.Millisecond}) 200 | for _, tt := range tests { 201 | t.Run(tt.name, func(t *testing.T) { 202 | var jsonPayload []byte 203 | repoName := "ossf-tests/scorecard-action" 204 | repoRef := "refs/heads/main" 205 | //nolint:gosec // dummy credentials 206 | accessToken := "ghs_foo" 207 | var nRequests int 208 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 209 | nRequests++ 210 | status := http.StatusCreated 211 | if tt.nFailures > 0 { 212 | status = http.StatusBadRequest 213 | tt.nFailures-- 214 | } 215 | w.WriteHeader(status) 216 | })) 217 | t.Setenv(options.EnvInputInternalPublishBaseURL, server.URL) 218 | t.Cleanup(server.Close) 219 | 220 | s, err := New(accessToken) 221 | if err != nil { 222 | t.Fatalf("Unexpected error New: %v", err) 223 | } 224 | err = s.ProcessSignature(jsonPayload, repoName, repoRef) 225 | if (err != nil) != tt.wantErr { 226 | t.Errorf("ProcessSignature() error: %v, wantErr: %v", err, tt.wantErr) 227 | } 228 | if nRequests != tt.wantNRequests { 229 | t.Errorf("ProcessSignature() made %d requests, wanted %d", nRequests, tt.wantNRequests) 230 | } 231 | }) 232 | } 233 | } 234 | 235 | // temporarily sets the backoffs for a given test. 236 | func setBackoffs(t *testing.T, newBackoffs []time.Duration) { 237 | t.Helper() 238 | old := backoffSchedule 239 | backoffSchedule = newBackoffs 240 | t.Cleanup(func() { 241 | backoffSchedule = old 242 | }) 243 | } 244 | -------------------------------------------------------------------------------- /signing/testdata/cosign.bundle: -------------------------------------------------------------------------------- 1 | {"base64Signature":"MEUCIQD4rAvIUYt0WgrXVL6whlkgJerX2jyRNDct3AwWSsGdJgIgP0DCR56AM9FQD/aEnZwnNT1s22V8PW/ud0YHREr2n0U=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMwakNDQWxlZ0F3SUJBZ0lVYzRvZkdaWUlxSEMvaW0xY0JPQ0FXbk9BQUQ4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpNd05qRXlNakV5TlRNMldoY05Nak13TmpFeU1qRXpOVE0yV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVzUGd2b2k0b3ZyN3dLRWxtRkR0R2VqZHBwTXFEMkZtc2tHQ2EKUFNLNlVSRUlhQ3h2Q1NrRlNwQnE1bmNhd0lscWtnMVBnKy9sMHhhcDExd0hrOWplNEtPQ0FYWXdnZ0Z5TUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVBNkI1CmE4TXdjS0M4UFdMNjc4UGF3N0FTY3Rjd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0lRWURWUjBSQVFIL0JCY3dGWUVUYzNOamFISnZZMnRBWjI5dloyeGxMbU52YlRBc0Jnb3JCZ0VFQVlPLwpNQUVCQkI1b2RIUndjem92TDJkcGRHaDFZaTVqYjIwdmJHOW5hVzR2YjJGMWRHZ3dMZ1lLS3dZQkJBR0R2ekFCCkNBUWdEQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZa0dDaXNHQVFRQjFua0MKQkFJRWV3UjVBSGNBZFFEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnVqZ0FBQVlpeApnQUl3QUFBRUF3QkdNRVFDSUJTN3lEOERVUTN2b3RnRXFCZzhXekFqbmFkNjJGRVgvZXZCUkNnZTdmeWpBaUJECkZseG8vOC9OVWNvN3VRSkNSazgrbDUvQXN5dG1jNU5vL3l0OW9tYkg5ekFLQmdncWhrak9QUVFEQXdOcEFEQm0KQWpFQWhjT1hBSmNyWGovWnlTYi9DYUY4aldSckE4bkthRWllRkREVFI0dFhKakk2bVE0ZEM2R05mWU8xdnRpMQpDUU1pQWpFQW9PWU44dnRDOEt5b3BWR3F1MkUxaHJuWFlCUXJUK1I4eFN5OEZRMHNTQVhGYzU0aGM0S2ZPemthClJIVXZlUUZSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEQCIG+3Fj1/9aYBP2fp9gbI9RpOc1/9aawUip5+aflRrtn/AiAo2nuSf3IIFwHpAgQL8eLkA3++gRiUkXDJzRZqjCXRhA==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiOWJiNzc2MGEyYjNiOWQwZjA4ZTI5OGVjYjg3Y2ZmMmQxMzkxMjJlM2Y2ZDI1ODI5MGZiODA3YmYxYWZlNjkyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUQ0ckF2SVVZdDBXZ3JYVkw2d2hsa2dKZXJYMmp5Uk5EY3QzQXdXU3NHZEpnSWdQMERDUjU2QU05RlFEL2FFblp3bk5UMXMyMlY4UFcvdWQwWUhSRXIybjBVPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTXdha05EUVd4bFowRjNTVUpCWjBsVll6UnZaa2RhV1VseFNFTXZhVzB4WTBKUFEwRlhiazlCUVVRNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcE5kMDVxUlhsTmFrVjVUbFJOTWxkb1kwNU5hazEzVG1wRmVVMXFSWHBPVkUweVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ6VUdkMmIyazBiM1p5TjNkTFJXeHRSa1IwUjJWcVpIQndUWEZFTWtadGMydEhRMkVLVUZOTE5sVlNSVWxoUTNoMlExTnJSbE53UW5FMWJtTmhkMGxzY1d0bk1WQm5LeTlzTUhoaGNERXhkMGhyT1dwbE5FdFBRMEZZV1hkblowWjVUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZCTmtJMUNtRTRUWGRqUzBNNFVGZE1OamM0VUdGM04wRlRZM1JqZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsUldVUldVakJTUVZGSUwwSkNZM2RHV1VWVVl6Tk9hbUZJU25aWk1uUkJXakk1ZGxveWVHeE1iVTUyWWxSQmMwSm5iM0pDWjBWRlFWbFBMd3BOUVVWQ1FrSTFiMlJJVW5kamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RtSkhPVzVoVnpSMllqSkdNV1JIWjNkTVoxbExTM2RaUWtKQlIwUjJla0ZDQ2tOQlVXZEVRalZ2WkVoU2QyTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2M1Ym1GWE5IWmlNa1l4WkVkbmQyZFphMGREYVhOSFFWRlJRakZ1YTBNS1FrRkpSV1YzVWpWQlNHTkJaRkZFWkZCVVFuRjRjMk5TVFcxTldraG9lVnBhZW1ORGIydHdaWFZPTkRoeVppdElhVzVMUVV4NWJuVnFaMEZCUVZscGVBcG5RVWwzUVVGQlJVRjNRa2ROUlZGRFNVSlROM2xFT0VSVlVUTjJiM1JuUlhGQ1p6aFhla0ZxYm1Ga05qSkdSVmd2WlhaQ1VrTm5aVGRtZVdwQmFVSkVDa1pzZUc4dk9DOU9WV052TjNWUlNrTlNhemdyYkRVdlFYTjVkRzFqTlU1dkwzbDBPVzl0WWtnNWVrRkxRbWRuY1docmFrOVFVVkZFUVhkT2NFRkVRbTBLUVdwRlFXaGpUMWhCU21OeVdHb3ZXbmxUWWk5RFlVWTRhbGRTY2tFNGJrdGhSV2xsUmtSRVZGSTBkRmhLYWtrMmJWRTBaRU0yUjA1bVdVOHhkblJwTVFwRFVVMXBRV3BGUVc5UFdVNDRkblJET0V0NWIzQldSM0YxTWtVeGFISnVXRmxDVVhKVUsxSTRlRk41T0VaUk1ITlRRVmhHWXpVMGFHTTBTMlpQZW10aENsSklWWFpsVVVaU0NpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19","integratedTime":1686605136,"logIndex":23548006,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}} -------------------------------------------------------------------------------- /signing/testdata/invalid-cosign.bundle: -------------------------------------------------------------------------------- 1 | {"base64Signature":"MEUCIQD4rAvIUYt0WgrXVL6whlkgJerX2jyRNDct3AwWSsGdJgIgP0DCR56AM9FQD/aEnZwnNT1s22V8PW/ud0YHREr2n0U=","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMwakNDQWxlZ0F3SUJBZ0lVYzRvZkdaWUlxSEMvaW0xY0JPQ0FXbk9BQUQ4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpNd05qRXlNakV5TlRNMldoY05Nak13TmpFeU1qRXpOVE0yV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVzUGd2b2k0b3ZyN3dLRWxtRkR0R2VqZHBwTXFEMkZtc2tHQ2EKUFNLNlVSRUlhQ3h2Q1NrRlNwQnE1bmNhd0lscWtnMVBnKy9sMHhhcDExd0hrOWplNEtPQ0FYWXdnZ0Z5TUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVBNkI1CmE4TXdjS0M4UFdMNjc4UGF3N0FTY3Rjd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0lRWURWUjBSQVFIL0JCY3dGWUVUYzNOamFISnZZMnRBWjI5dloyeGxMbU52YlRBc0Jnb3JCZ0VFQVlPLwpNQUVCQkI1b2RIUndjem92TDJkcGRHaDFZaTVqYjIwdmJHOW5hVzR2YjJGMWRHZ3dMZ1lLS3dZQkJBR0R2ekFCCkNBUWdEQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZa0dDaXNHQVFRQjFua0MKQkFJRWV3UjVBSGNBZFFEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnVqZ0FBQVlpeApnQUl3QUFBRUF3QkdNRVFDSUJTN3lEOERVUTN2b3RnRXFCZzhXekFqbmFkNjJGRVgvZXZCUkNnZTdmeWpBaUJECkZseG8vOC9OVWNvN3VRSkNSazgrbDUvQXN5dG1jNU5vL3l0OW9tYkg5ekFLQmdncWhrak9QUVFEQXdOcEFEQm0KQWpFQWhjT1hBSmNyWGovWnlTYi9DYUY4aldSckE4bkthRWllRkREVFI0dFhKakk2bVE0ZEM2R05mWU8xdnRpMQpDUU1pQWpFQW9PWU44dnRDOEt5b3BWR3F1MkUxaHJuWFlCUXJUK1I4eFN5OEZRMHNTQVhGYzU0aGM0S2ZPemthClJIVXZlUUZSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"} -------------------------------------------------------------------------------- /signing/testdata/results.json: -------------------------------------------------------------------------------- 1 | {"date":"2022-06-01","repo":{"name":"github.com/ossf-tests/scorecard-action","commit":"ce7443af32a20ce3e55d18da9ae434364f04b450"},"scorecard":{"version":"unknown","commit":"unknown"},"score":5.8,"checks":[{"details":null,"score":10,"reason":"no binaries found in the repo","name":"Binary-Artifacts","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#binary-artifacts","short":"Determines if the project has generated executable (binary) artifacts in the source repository."}},{"details":null,"score":0,"reason":"branch protection not enabled on development/release branches","name":"Branch-Protection","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection","short":"Determines if the default and release branches are protected with GitHub's branch protection settings."}},{"details":null,"score":10,"reason":"1 out of 1 merged PRs checked by a CI test -- score normalized to 10","name":"CI-Tests","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#ci-tests","short":"Determines if the project runs tests before pull requests are merged."}},{"details":null,"score":0,"reason":"no badge detected","name":"CII-Best-Practices","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#cii-best-practices","short":"Determines if the project has a CII Best Practices Badge."}},{"details":null,"score":0,"reason":"GitHub code reviews found for 1 commits out of the last 30 -- score normalized to 0","name":"Code-Review","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#code-review","short":"Determines if the project requires code review before pull requests (aka merge requests) are merged."}},{"details":null,"score":10,"reason":"5 different companies found -- score normalized to 10","name":"Contributors","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#contributors","short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies)."}},{"details":null,"score":10,"reason":"no dangerous workflow patterns detected","name":"Dangerous-Workflow","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#dangerous-workflow","short":"Determines if the project's GitHub Action workflows avoid dangerous patterns."}},{"details":null,"score":10,"reason":"update tool detected","name":"Dependency-Update-Tool","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool","short":"Determines if the project uses a dependency update tool."}},{"details":null,"score":0,"reason":"project is not fuzzed","name":"Fuzzing","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#fuzzing","short":"Determines if the project uses fuzzing."}},{"details":null,"score":0,"reason":"license file not detected","name":"License","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#license","short":"Determines if the project has defined a license."}},{"details":null,"score":10,"reason":"30 commit(s) out of 30 and 0 issue activity out of 0 found in the last 90 days -- score normalized to 10","name":"Maintained","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained","short":"Determines if the project is \"actively maintained\"."}},{"details":null,"score":-1,"reason":"no published package detected","name":"Packaging","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#packaging","short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall."}},{"details":null,"score":8,"reason":"dependency not pinned by hash detected -- score normalized to 8","name":"Pinned-Dependencies","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies","short":"Determines if the project has declared and pinned its dependencies."}},{"details":null,"score":10,"reason":"SAST tool is run on all commits","name":"SAST","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#sast","short":"Determines if the project uses static code analysis."}},{"details":null,"score":0,"reason":"security policy file not detected","name":"Security-Policy","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#security-policy","short":"Determines if the project has published a security policy."}},{"details":null,"score":-1,"reason":"no releases found","name":"Signed-Releases","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#signed-releases","short":"Determines if the project cryptographically signs release artifacts."}},{"details":null,"score":0,"reason":"non read-only tokens detected in GitHub workflows","name":"Token-Permissions","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions","short":"Determines if the project's workflows follow the principle of least privilege."}},{"details":null,"score":10,"reason":"no vulnerabilities detected","name":"Vulnerabilities","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#vulnerabilities","short":"Determines if the project has open, known unfixed vulnerabilities."}},{"details":null,"score":-1,"reason":"check is not supported for this request: SCORECARD_V6 is not set, not running the Webhook check","name":"Webhooks","documentation":{"url":"https://github.com/ossf/scorecard/blob/main/docs/checks.md#webhooks","short":"This check validate if the webhook defined in the repository have a token configured."}}],"metadata":null} 2 | --------------------------------------------------------------------------------