Skip to content

Commit ef58387

Browse files
authored
Merge pull request #62 from secure-dashboards/feat/add-ossf-scorecard
2 parents 6f0694f + 513a28a commit ef58387

File tree

17 files changed

+593
-21
lines changed

17 files changed

+593
-21
lines changed

__fixtures__/index.js

+201-1
Original file line numberDiff line numberDiff line change
@@ -710,8 +710,208 @@ const sampleGithubRepository = {
710710
}
711711
}
712712

713+
const sampleOSSFScorecardResult = {
714+
date: '2024-12-11T23:55:17Z',
715+
repo: {
716+
name: 'github.com/octocat/Hello-World',
717+
commit: 'e739f419e56442b754e4fea6dbcf98c1c8d00dda'
718+
},
719+
scorecard: {
720+
version: 'v5.0.0',
721+
commit: 'ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4'
722+
},
723+
score: 6,
724+
checks: [
725+
{
726+
details: null,
727+
score: 10,
728+
reason: 'no binaries found in the repo',
729+
name: 'Binary-Artifacts',
730+
documentation: {
731+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#binary-artifacts',
732+
short: 'Determines if the project has generated executable (binary) artifacts in the source repository.'
733+
}
734+
},
735+
{
736+
details: null,
737+
score: 3,
738+
reason: 'branch protection is not maximal on development and all release branches',
739+
name: 'Branch-Protection',
740+
documentation: {
741+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#branch-protection',
742+
short: "Determines if the default and release branches are protected with GitHub's branch protection settings."
743+
}
744+
},
745+
{
746+
details: null,
747+
score: 6,
748+
reason: '13 out of 19 merged PRs checked by a CI test -- score normalized to 6',
749+
name: 'CI-Tests',
750+
documentation: {
751+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#ci-tests',
752+
short: 'Determines if the project runs tests before pull requests are merged.'
753+
}
754+
},
755+
{
756+
details: null,
757+
score: 0,
758+
reason: 'no effort to earn an OpenSSF best practices badge detected',
759+
name: 'CII-Best-Practices',
760+
documentation: {
761+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#cii-best-practices',
762+
short: 'Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.'
763+
}
764+
},
765+
{
766+
details: null,
767+
score: 3,
768+
reason: 'Found 9/30 approved changesets -- score normalized to 3',
769+
name: 'Code-Review',
770+
documentation: {
771+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#code-review',
772+
short: 'Determines if the project requires human code review before pull requests (aka merge requests) are merged.'
773+
}
774+
},
775+
{
776+
details: null,
777+
score: 10,
778+
reason: 'project has 21 contributing companies or organizations',
779+
name: 'Contributors',
780+
documentation: {
781+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#contributors',
782+
short: 'Determines if the project has a set of contributors from multiple organizations (e.g., companies).'
783+
}
784+
},
785+
{
786+
details: null,
787+
score: 10,
788+
reason: 'no dangerous workflow patterns detected',
789+
name: 'Dangerous-Workflow',
790+
documentation: {
791+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#dangerous-workflow',
792+
short: "Determines if the project's GitHub Action workflows avoid dangerous patterns."
793+
}
794+
},
795+
{
796+
details: null,
797+
score: 0,
798+
reason: 'no update tool detected',
799+
name: 'Dependency-Update-Tool',
800+
documentation: {
801+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#dependency-update-tool',
802+
short: 'Determines if the project uses a dependency update tool.'
803+
}
804+
},
805+
{
806+
details: null,
807+
score: 0,
808+
reason: 'project is not fuzzed',
809+
name: 'Fuzzing',
810+
documentation: {
811+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#fuzzing',
812+
short: 'Determines if the project uses fuzzing.'
813+
}
814+
},
815+
{
816+
score: 10,
817+
reason: 'license file detected',
818+
name: 'License',
819+
details: [
820+
'Info: project has a license file: LICENSE:0',
821+
'Info: FSF or OSI recognized license: MIT License: LICENSE:0'
822+
],
823+
documentation: {
824+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#license',
825+
short: 'Determines if the project has defined a license.'
826+
}
827+
},
828+
{
829+
details: null,
830+
score: 10,
831+
reason: '30 commit(s) and 21 issue activity found in the last 90 days -- score normalized to 10',
832+
name: 'Maintained',
833+
documentation: {
834+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#maintained',
835+
short: 'Determines if the project is "actively maintained".'
836+
}
837+
},
838+
{
839+
details: null,
840+
score: -1,
841+
reason: 'packaging workflow not detected',
842+
name: 'Packaging',
843+
documentation: {
844+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#packaging',
845+
short: 'Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.'
846+
}
847+
},
848+
{
849+
details: null,
850+
score: 4,
851+
reason: 'dependency not pinned by hash detected -- score normalized to 4',
852+
name: 'Pinned-Dependencies',
853+
documentation: {
854+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#pinned-dependencies',
855+
short: 'Determines if the project has declared and pinned the dependencies of its build process.'
856+
}
857+
},
858+
{
859+
details: null,
860+
score: 0,
861+
reason: 'SAST tool is not run on all commits -- score normalized to 0',
862+
name: 'SAST',
863+
documentation: {
864+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#sast',
865+
short: 'Determines if the project uses static code analysis.'
866+
}
867+
},
868+
{
869+
details: null,
870+
score: 10,
871+
reason: 'security policy file detected',
872+
name: 'Security-Policy',
873+
documentation: {
874+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#security-policy',
875+
short: 'Determines if the project has published a security policy.'
876+
}
877+
},
878+
{
879+
details: null,
880+
score: -1,
881+
reason: 'no releases found',
882+
name: 'Signed-Releases',
883+
documentation: {
884+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#signed-releases',
885+
short: 'Determines if the project cryptographically signs release artifacts.'
886+
}
887+
},
888+
{
889+
details: null,
890+
score: 9,
891+
reason: 'detected GitHub workflow tokens with excessive permissions',
892+
name: 'Token-Permissions',
893+
documentation: {
894+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#token-permissions',
895+
short: "Determines if the project's workflows follow the principle of least privilege."
896+
}
897+
},
898+
{
899+
details: null,
900+
score: 8,
901+
reason: '2 existing vulnerabilities detected',
902+
name: 'Vulnerabilities',
903+
documentation: {
904+
url: 'https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#vulnerabilities',
905+
short: 'Determines if the project has open, known unfixed vulnerabilities.'
906+
}
907+
}
908+
],
909+
metadata: null
910+
}
911+
713912
module.exports = {
714913
sampleGithubOrg,
715914
sampleGithubListOrgRepos,
716-
sampleGithubRepository
915+
sampleGithubRepository,
916+
sampleOSSFScorecardResult
717917
}

__tests__/__snapshots__/providers.test.js.snap

+104-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`GitHub Providers mappers Should map organization data correctly 1`] = `
3+
exports[`GitHub Provider mappers Should map organization data correctly 1`] = `
44
{
55
"advanced_security_enabled_for_new_repositories": false,
66
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
@@ -53,7 +53,7 @@ exports[`GitHub Providers mappers Should map organization data correctly 1`] = `
5353
}
5454
`;
5555

56-
exports[`GitHub Providers mappers Should map repository data correctly 1`] = `
56+
exports[`GitHub Provider mappers Should map repository data correctly 1`] = `
5757
{
5858
"allow_auto_merge": false,
5959
"allow_forking": true,
@@ -114,3 +114,105 @@ exports[`GitHub Providers mappers Should map repository data correctly 1`] = `
114114
"watchers_count": 80,
115115
}
116116
`;
117+
118+
exports[`OSSF Provider mappers Should map scorecard data correctly 1`] = `
119+
{
120+
"analysis_execution_time": 19876,
121+
"analysis_score": 6,
122+
"analysis_time": "2024-12-11T23:55:17Z",
123+
"binary_artifacts_details": undefined,
124+
"binary_artifacts_documentation": "Determines if the project has generated executable (binary) artifacts in the source repository.",
125+
"binary_artifacts_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#binary-artifacts",
126+
"binary_artifacts_reason": "no binaries found in the repo",
127+
"binary_artifacts_score": 10,
128+
"branch_protection_details": undefined,
129+
"branch_protection_documentation": "Determines if the default and release branches are protected with GitHub's branch protection settings.",
130+
"branch_protection_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#branch-protection",
131+
"branch_protection_reason": "branch protection is not maximal on development and all release branches",
132+
"branch_protection_score": 3,
133+
"ci_tests_details": undefined,
134+
"ci_tests_documentation": "Determines if the project runs tests before pull requests are merged.",
135+
"ci_tests_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#ci-tests",
136+
"ci_tests_reason": "13 out of 19 merged PRs checked by a CI test -- score normalized to 6",
137+
"ci_tests_score": 6,
138+
"cii_best_practices_details": undefined,
139+
"cii_best_practices_documentation": "Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.",
140+
"cii_best_practices_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#cii-best-practices",
141+
"cii_best_practices_reason": "no effort to earn an OpenSSF best practices badge detected",
142+
"cii_best_practices_score": 0,
143+
"code_review_details": undefined,
144+
"code_review_documentation": "Determines if the project requires human code review before pull requests (aka merge requests) are merged.",
145+
"code_review_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#code-review",
146+
"code_review_reason": "Found 9/30 approved changesets -- score normalized to 3",
147+
"code_review_score": 3,
148+
"contributors_details": undefined,
149+
"contributors_documentation": "Determines if the project has a set of contributors from multiple organizations (e.g., companies).",
150+
"contributors_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#contributors",
151+
"contributors_reason": "project has 21 contributing companies or organizations",
152+
"contributors_score": 10,
153+
"dangerous_workflow_details": undefined,
154+
"dangerous_workflow_documentation": "Determines if the project's GitHub Action workflows avoid dangerous patterns.",
155+
"dangerous_workflow_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#dangerous-workflow",
156+
"dangerous_workflow_reason": "no dangerous workflow patterns detected",
157+
"dangerous_workflow_score": 10,
158+
"dependency_update_tool_details": undefined,
159+
"dependency_update_tool_documentation": "Determines if the project uses a dependency update tool.",
160+
"dependency_update_tool_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#dependency-update-tool",
161+
"dependency_update_tool_reason": "no update tool detected",
162+
"dependency_update_tool_score": 0,
163+
"fuzzing_details": undefined,
164+
"fuzzing_documentation": "Determines if the project uses fuzzing.",
165+
"fuzzing_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#fuzzing",
166+
"fuzzing_reason": "project is not fuzzed",
167+
"fuzzing_score": 0,
168+
"license_details": "Info: project has a license file: LICENSE:0
169+
Info: FSF or OSI recognized license: MIT License: LICENSE:0",
170+
"license_documentation": "Determines if the project has defined a license.",
171+
"license_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#license",
172+
"license_reason": "license file detected",
173+
"license_score": 10,
174+
"maintained_details": undefined,
175+
"maintained_documentation": "Determines if the project is "actively maintained".",
176+
"maintained_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#maintained",
177+
"maintained_reason": "30 commit(s) and 21 issue activity found in the last 90 days -- score normalized to 10",
178+
"maintained_score": 10,
179+
"packaging_details": undefined,
180+
"packaging_documentation": "Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.",
181+
"packaging_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#packaging",
182+
"packaging_reason": "packaging workflow not detected",
183+
"packaging_score": -1,
184+
"pinned_dependencies_details": undefined,
185+
"pinned_dependencies_documentation": "Determines if the project has declared and pinned the dependencies of its build process.",
186+
"pinned_dependencies_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#pinned-dependencies",
187+
"pinned_dependencies_reason": "dependency not pinned by hash detected -- score normalized to 4",
188+
"pinned_dependencies_score": 4,
189+
"repo_commit": "e739f419e56442b754e4fea6dbcf98c1c8d00dda",
190+
"sast_details": undefined,
191+
"sast_documentation": "Determines if the project uses static code analysis.",
192+
"sast_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#sast",
193+
"sast_reason": "SAST tool is not run on all commits -- score normalized to 0",
194+
"sast_score": 0,
195+
"scorecard_commit": "ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4",
196+
"scorecard_version": "v5.0.0",
197+
"security_policy_details": undefined,
198+
"security_policy_documentation": "Determines if the project has published a security policy.",
199+
"security_policy_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#security-policy",
200+
"security_policy_reason": "security policy file detected",
201+
"security_policy_score": 10,
202+
"signed_releases_details": undefined,
203+
"signed_releases_documentation": "Determines if the project cryptographically signs release artifacts.",
204+
"signed_releases_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#signed-releases",
205+
"signed_releases_reason": "no releases found",
206+
"signed_releases_score": -1,
207+
"token_permissions_details": undefined,
208+
"token_permissions_documentation": "Determines if the project's workflows follow the principle of least privilege.",
209+
"token_permissions_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#token-permissions",
210+
"token_permissions_reason": "detected GitHub workflow tokens with excessive permissions",
211+
"token_permissions_score": 9,
212+
"vulnerabilities_details": undefined,
213+
"vulnerabilities_documentation": "Determines if the project has open, known unfixed vulnerabilities.",
214+
"vulnerabilities_documentation_url": "https://github.com/ossf/scorecard/blob/ea7e27ed41b76ab879c862fa0ca4cc9c61764ee4/docs/checks.md#vulnerabilities",
215+
"vulnerabilities_reason": "2 existing vulnerabilities detected",
216+
"vulnerabilities_score": 8,
217+
}
218+
`;

__tests__/cli/__snapshots__/workflows.test.js.snap

+5
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,10 @@ exports[`list - Non-Interactive Mode Should provide a list of available workflow
1717
"name": "run-all-checks",
1818
"workflow": [Function],
1919
},
20+
{
21+
"description": "Upsert the OSSF Scorecard scoring by running and checking every repository in the database.",
22+
"name": "upsert-ossf-scorecard",
23+
"workflow": [Function],
24+
},
2025
]
2126
`;

__tests__/cli/workflows.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,7 @@ describe('run upsert-github-repositories', () => {
140140
describe('run run-all-checks', () => {
141141
test.todo('Should run all the compliance checks for the stored data')
142142
})
143+
144+
describe('run upsert-ossf-scorecard', () => {
145+
test.todo('Should upsert the OSSF Scorecard scoring by running and checking every repository in the database')
146+
})

__tests__/providers.test.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
const { github } = require('../src/providers')
1+
const { github, ossf } = require('../src/providers')
22
const {
33
sampleGithubOrg,
4-
sampleGithubRepository
4+
sampleGithubRepository,
5+
sampleOSSFScorecardResult
56
} = require('../__fixtures__')
67

7-
describe('GitHub Providers', () => {
8+
describe('GitHub Provider', () => {
89
describe('fetchOrgByLogin', () => {
910
it.todo('Should fetch organization by login')
1011
it.todo('Should throw an error if the organization does not exist')
@@ -35,3 +36,17 @@ describe('GitHub Providers', () => {
3536
})
3637
})
3738
})
39+
40+
describe('OSSF Provider', () => {
41+
describe('performScorecardAnalysis', () => {
42+
it.todo('Should perform scorecard analysis')
43+
it.todo('Should throw an error if the repository does not exist')
44+
it.todo('Should throw an error if there are network issues')
45+
})
46+
describe('mappers', () => {
47+
it('Should map scorecard data correctly', () => {
48+
const mappedData = ossf.mappers.result({ ...sampleOSSFScorecardResult, analysis_execution_time: 19876 })
49+
expect(mappedData).toMatchSnapshot()
50+
})
51+
})
52+
})

0 commit comments

Comments
 (0)