-
Notifications
You must be signed in to change notification settings - Fork 85
feat(scorecard): add OpenSSF scorecard #1952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
alizard0
wants to merge
13
commits into
redhat-developer:main
Choose a base branch
from
alizard0:add-scorecard-backend-module-openssf
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6783eba
implemented abstract openssf metric provider and its implementations
alizard0 b84d08e
feat: make OpenSSFClient baseUrl and gitServiceHost configurable; add…
alizard0 450506a
feat: fix openssf annotation
alizard0 ec8024c
use createOpenSSFMetricProviders
JessicaJHee 4eb490d
refactored the implemented classes for OpenSSFMetric providers; revie…
alizard0 f7f50da
fixed 4 ts errors reported by yarn tsc:full
alizard0 ecd0931
add api-report
alizard0 f9ba9dc
review work
alizard0 3b7c562
Throws error when metric score is lower than 0 or higher than 10; Add…
alizard0 8806a2e
review work
alizard0 0a5f836
Update workspaces/scorecard/examples/openssf-scorecard-only.yaml
alizard0 fe5b1f9
Update workspaces/scorecard/examples/openssf-scorecard-only.yaml
alizard0 f49e0fb
removed extra whitespace
alizard0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| --- | ||
| # Component with OpenSSF Scorecard | ||
| apiVersion: backstage.io/v1alpha1 | ||
| kind: Component | ||
| metadata: | ||
| name: openssf-scorecard-only | ||
| annotations: | ||
| github.com/project-slug: backstage/backstage | ||
| openssf/project: backstage/backstage | ||
| backstage.io/source-location: url:https://github.com/backstage/backstage | ||
| spec: | ||
| type: service | ||
| owner: guests | ||
| lifecycle: experimental |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
workspaces/scorecard/plugins/scorecard-backend-module-openssf/.eslintrc.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); |
127 changes: 127 additions & 0 deletions
127
workspaces/scorecard/plugins/scorecard-backend-module-openssf/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # Scorecard Backend Module for OpenSSF | ||
|
|
||
| This is an extension module to the `backstage-plugin-scorecard-backend` plugin. It provides [OpenSSF Security Scorecard](https://securityscorecards.dev/) metrics for software components registered in the Backstage catalog. | ||
|
|
||
| ## Overview | ||
|
|
||
| The OpenSSF Security Scorecards project provides automated security assessments for open source projects hosted on GitHub. This module fetches scorecard data from the public OpenSSF API and exposes individual security check scores as metrics in Backstage. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Before installing this module, ensure that the Scorecard backend plugin is integrated into your Backstage instance. Follow the [Scorecard backend plugin README](../scorecard-backend/README.md) for setup instructions. | ||
|
|
||
| ## Installation | ||
|
|
||
| To install this backend module: | ||
|
|
||
| ```bash | ||
| # From your root directory | ||
| yarn workspace backend add @red-hat-developer-hub/backstage-plugin-scorecard-backend-module-openssf | ||
| ``` | ||
|
|
||
| ```ts | ||
| // packages/backend/src/index.ts | ||
| import { createBackend } from '@backstage/backend-defaults'; | ||
|
|
||
| const backend = createBackend(); | ||
|
|
||
| // Scorecard backend plugin | ||
| backend.add( | ||
| import('@red-hat-developer-hub/backstage-plugin-scorecard-backend'), | ||
| ); | ||
|
|
||
| // Install the OpenSSF module | ||
| /* highlight-add-next-line */ | ||
| backend.add( | ||
| import( | ||
| '@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-openssf' | ||
| ), | ||
| ); | ||
|
|
||
| backend.start(); | ||
| ``` | ||
|
|
||
| ## Entity Annotations | ||
|
|
||
| For the OpenSSF metrics to work, your catalog entities must have the required annotation: | ||
|
|
||
| ```yaml | ||
| # catalog-info.yaml | ||
| apiVersion: backstage.io/v1alpha1 | ||
| kind: Component | ||
| metadata: | ||
| name: my-service | ||
| annotations: | ||
| # Required: GitHub repository in owner/repo format | ||
| openssf/project: owner/repo | ||
| spec: | ||
| type: service | ||
| lifecycle: production | ||
| owner: my-team | ||
| ``` | ||
|
|
||
| The `openssf/project` annotation should contain the GitHub repository path in `owner/repo` format (e.g., `kubernetes/kubernetes`). | ||
|
|
||
| ## Configuration | ||
|
|
||
| This module uses the public OpenSSF Security Scorecards API (`api.securityscorecards.dev`) and does not require any additional configuration in `app-config.yaml`. | ||
|
|
||
| ### Thresholds | ||
|
|
||
| Thresholds define conditions that determine which category a metric value belongs to (`error`, `warning`, or `success`). Check out detailed explanation of [threshold configuration](../scorecard-backend/docs/thresholds.md). | ||
|
|
||
| All OpenSSF metrics use the following **fixed** thresholds: | ||
|
|
||
| | Category | Expression | Description | | ||
| | -------- | ---------- | --------------------------------- | | ||
| | Error | `<2` | Score less than 2 | | ||
| | Warning | `2-7` | Score between 2 and 7 (inclusive) | | ||
| | Success | `>7` | Score greater than 7 | | ||
|
|
||
| > **Note:** These thresholds are not configurable via `app-config.yaml`. They are defined in the module source code. | ||
|
|
||
| ## Available Metrics | ||
|
|
||
| This module provides 18 metrics corresponding to the [OpenSSF Security Scorecard checks](https://github.com/ossf/scorecard/blob/main/docs/checks.md). Each metric returns a score from 0 to 10. | ||
|
|
||
| | Metric ID | Risk | Description | | ||
| | -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | | ||
| | `openssf.binary_artifacts` | High | Determines if the project has generated executable (binary) artifacts in the source repository. | | ||
| | `openssf.branch_protection` | High | Determines if the default and release branches are protected with GitHub's branch protection or repository rules settings. | | ||
| | `openssf.cii_best_practices` | Low | Determines if the project has an OpenSSF (formerly CII) Best Practices Badge. | | ||
| | `openssf.ci_tests` | Low | Determines if the project runs tests before pull requests are merged. | | ||
| | `openssf.code_review` | High | Determines if the project requires human code review before pull requests are merged. | | ||
| | `openssf.contributors` | Low | Determines if the project has contributors from multiple organizations. | | ||
| | `openssf.dangerous_workflow` | Critical | Determines if the project's GitHub Action workflows avoid dangerous patterns. | | ||
| | `openssf.dependency_update_tool` | High | Determines if the project uses a dependency update tool. | | ||
| | `openssf.fuzzing` | Medium | Determines if the project uses fuzzing. | | ||
| | `openssf.license` | Low | Determines if the project has defined a license. | | ||
| | `openssf.maintained` | High | Determines if the project is "actively maintained". | | ||
| | `openssf.packaging` | Medium | Determines if the project is published as a package that others can easily download, install, update, and uninstall. | | ||
| | `openssf.pinned_dependencies` | Medium | Determines if the project has declared and pinned the dependencies of its build process. | | ||
| | `openssf.sast` | Medium | Determines if the project uses static code analysis. | | ||
| | `openssf.security_policy` | Medium | Determines if the project has published a security policy. | | ||
| | `openssf.signed_releases` | High | Determines if the project cryptographically signs release artifacts. | | ||
| | `openssf.token_permissions` | High | Determines if the project's automated workflow tokens follow the principle of least privilege. | | ||
| | `openssf.vulnerabilities` | High | Determines if the project has open, unfixed vulnerabilities in its codebase or dependencies using OSV. | | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Metric shows "not found" | ||
|
|
||
| This can occur if: | ||
|
|
||
| - The repository has not been analyzed by OpenSSF Scorecards yet | ||
| - The repository is private (OpenSSF only analyzes public repositories) | ||
| - The repository path in the annotation is incorrect | ||
| - The metric score is lower than -1 or higher than 10. | ||
|
|
||
| ### No data for my repository | ||
|
|
||
| OpenSSF Security Scorecards only analyzes **public GitHub repositories**. Private repositories and repositories on other Git hosting services are not supported. | ||
|
|
||
| To verify your repository has scorecard data, visit: | ||
|
|
||
| ``` | ||
| https://api.securityscorecards.dev/projects/github.com/{owner}/{repo} | ||
| ``` |
47 changes: 47 additions & 0 deletions
47
workspaces/scorecard/plugins/scorecard-backend-module-openssf/package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| { | ||
| "name": "@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-openssf", | ||
| "version": "0.1.0", | ||
| "license": "Apache-2.0", | ||
| "private": true, | ||
| "description": "The openssf backend module for the scorecard plugin.", | ||
| "main": "src/index.ts", | ||
| "types": "src/index.ts", | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "main": "dist/index.cjs.js", | ||
| "types": "dist/index.d.ts" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/redhat-developer/rhdh-plugins", | ||
| "directory": "workspaces/scorecard/plugins/scorecard-backend-module-openssf" | ||
| }, | ||
| "backstage": { | ||
| "role": "backend-plugin-module", | ||
| "pluginId": "scorecard", | ||
| "pluginPackage": "@red-hat-developer-hub/backstage-plugin-scorecard-backend" | ||
| }, | ||
| "scripts": { | ||
| "start": "backstage-cli package start", | ||
| "build": "backstage-cli package build", | ||
| "lint": "backstage-cli package lint", | ||
| "test": "backstage-cli package test", | ||
| "clean": "backstage-cli package clean", | ||
| "prepack": "backstage-cli package prepack", | ||
| "postpack": "backstage-cli package postpack" | ||
| }, | ||
| "dependencies": { | ||
| "@backstage/backend-plugin-api": "^1.5.0", | ||
| "@backstage/catalog-client": "^1.12.1", | ||
| "@backstage/catalog-model": "^1.7.6", | ||
| "@red-hat-developer-hub/backstage-plugin-scorecard-common": "workspace:^", | ||
| "@red-hat-developer-hub/backstage-plugin-scorecard-node": "workspace:^" | ||
| }, | ||
| "devDependencies": { | ||
| "@backstage/backend-test-utils": "^1.10.0", | ||
| "@backstage/cli": "^0.34.5" | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| ] | ||
| } |
11 changes: 11 additions & 0 deletions
11
workspaces/scorecard/plugins/scorecard-backend-module-openssf/report.api.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| ## API Report File for "@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-openssf" | ||
|
|
||
| > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
|
|
||
| ```ts | ||
| import { BackendFeature } from '@backstage/backend-plugin-api'; | ||
|
|
||
| // @public (undocumented) | ||
| const scorecardOpenSFFModule: BackendFeature; | ||
| export default scorecardOpenSFFModule; | ||
| ``` |
79 changes: 79 additions & 0 deletions
79
...aces/scorecard/plugins/scorecard-backend-module-openssf/src/clients/OpenSSFClient.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| /* | ||
| * Copyright Red Hat, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| import { OpenSSFClient } from './OpenSSFClient'; | ||
| import { OpenSSFResponse } from './types'; | ||
|
|
||
| describe('OpenSSFClient', () => { | ||
| let client: OpenSSFClient; | ||
|
|
||
| const mockOpenSSFResponse: OpenSSFResponse = { | ||
| date: '2024-01-15', | ||
| repo: { | ||
| name: 'github.com/owner/test', | ||
| commit: 'abc123', | ||
| }, | ||
| scorecard: { | ||
| version: '4.0.0', | ||
| commit: 'def456', | ||
| }, | ||
| score: 7.5, | ||
| checks: [], | ||
| }; | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| client = new OpenSSFClient(); | ||
| globalThis.fetch = jest.fn(); | ||
| }); | ||
|
|
||
| describe('getScorecard', () => { | ||
| it('should return the scorecard', async () => { | ||
| // mocked fetch behaviour for the test | ||
| (globalThis.fetch as jest.Mock).mockResolvedValue({ | ||
| ok: true, | ||
| json: jest.fn().mockResolvedValue(mockOpenSSFResponse), | ||
| }); | ||
|
|
||
| const scorecard = await client.getScorecard('owner', 'test'); | ||
| expect(scorecard).toEqual(mockOpenSSFResponse); | ||
| }); | ||
|
|
||
| it('should throw an error if the API returns a non-ok response', async () => { | ||
| // mock response from the API | ||
| (globalThis.fetch as jest.Mock).mockResolvedValue({ | ||
| ok: false, | ||
| status: 404, | ||
| statusText: 'Not Found', | ||
| }); | ||
|
|
||
| await expect(client.getScorecard('wrong', 'test')).rejects.toThrow( | ||
| 'OpenSSF API request failed with status 404: Not Found', | ||
| ); | ||
| }); | ||
|
|
||
| it('should throw an error if API request fails', async () => { | ||
| // mocked fetch behaviour for the test | ||
| (globalThis.fetch as jest.Mock).mockRejectedValue( | ||
| new Error('API request failed'), | ||
| ); | ||
|
|
||
| await expect(client.getScorecard('owner', 'test')).rejects.toThrow( | ||
| 'API request failed', | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
51 changes: 51 additions & 0 deletions
51
workspaces/scorecard/plugins/scorecard-backend-module-openssf/src/clients/OpenSSFClient.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /* | ||
| * Copyright Red Hat, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| import { OpenSSFResponse } from './types'; | ||
|
|
||
| export class OpenSSFClient { | ||
| private readonly baseUrl: string; | ||
| private readonly gitServiceHost: string; | ||
|
|
||
| constructor( | ||
| baseUrl: string = 'https://api.securityscorecards.dev/projects', | ||
| gitServiceHost: string = 'github.com', | ||
| ) { | ||
| this.baseUrl = baseUrl; | ||
| this.gitServiceHost = gitServiceHost; | ||
| } | ||
|
|
||
| async getScorecard(owner: string, repo: string): Promise<OpenSSFResponse> { | ||
| const apiUrl = `${this.baseUrl}/${this.gitServiceHost}/${owner}/${repo}`; | ||
|
|
||
| const response = await fetch(apiUrl, { | ||
| method: 'GET', | ||
| headers: { | ||
| Accept: 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error( | ||
| `OpenSSF API request failed with status ${response.status}: ${response.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| const data: OpenSSFResponse = await response.json(); | ||
|
|
||
| return data; | ||
| } | ||
| } | ||
42 changes: 42 additions & 0 deletions
42
workspaces/scorecard/plugins/scorecard-backend-module-openssf/src/clients/types.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright Red Hat, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| export interface OpenSSFCheckDocumentation { | ||
| short: string; | ||
| url: string; | ||
| } | ||
|
|
||
| export interface OpenSSFCheck { | ||
| name: string; | ||
| score: number; | ||
| reason: string | null; | ||
| details: string[] | null; | ||
| documentation: OpenSSFCheckDocumentation; | ||
| } | ||
|
|
||
| export interface OpenSSFResponse { | ||
| date: string; | ||
| repo: { | ||
| name: string; | ||
| commit: string; | ||
| }; | ||
| scorecard: { | ||
| version: string; | ||
| commit: string; | ||
| }; | ||
| score: number; | ||
| checks: OpenSSFCheck[]; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.