├── .github ├── dependabot.yml ├── other │ ├── comparator-diff1.png │ ├── comparator-diff2.png │ ├── demo1.gif │ ├── discrepancies.png │ ├── gif_comparator.gif │ └── gitlab-support.png └── workflows │ ├── codeql.yml │ ├── dependency-review.yml │ ├── deployment.yml │ ├── pr_check.yml │ └── scorecard.yml ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── cypress.config.ts ├── cypress ├── e2e │ ├── comparator.cy.ts │ └── visualizer.cy.ts ├── fixtures │ ├── 077fd7d83d7d41695137c1af5b9be1d72250e69e.json │ ├── 2ac5e9889aba461f5a54d320973d2574980d206b.json │ └── 50477fa35367bb76e5f56ac93d661b01a5578cec.json └── support │ ├── commands.ts │ └── e2e.ts ├── package-lock.json ├── package.json ├── prettierrc.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── components │ ├── Badge.tsx │ ├── Collapsable.tsx │ ├── CommonError.tsx │ ├── ComparatorDiff.tsx │ ├── Loading.tsx │ ├── NoAvailableDataMark.tsx │ ├── NotFound.tsx │ ├── ProjectComparator.tsx │ └── ProjectDetails.tsx ├── constants │ ├── checks.ts │ ├── commonErrorMessage.ts │ └── platforms.ts ├── index.css ├── index.tsx ├── react-app-env.d.ts ├── reportWebVitals.ts ├── setupTests.ts ├── styles │ ├── Badge.css │ ├── Collapsable.css │ ├── ComparatorDiff.css │ ├── Loading.css │ ├── NoAvailableDataMark.css │ └── ProjectDetails.css ├── types │ └── index.ts └── utils │ ├── comparator │ ├── areEqualElements.test.js │ ├── areEqualElements.tsx │ ├── getRefinedChecks.test.js │ ├── getRefinedChecks.tsx │ └── scoreChecker.tsx │ ├── formatDate.test.ts │ ├── formatDate.ts │ ├── getScorecardUrl.test.ts │ └── getScorecardUrl.ts └── tsconfig.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | 8 | - package-ecosystem: npm 9 | directory: / 10 | schedule: 11 | interval: daily 12 | -------------------------------------------------------------------------------- /.github/other/comparator-diff1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/comparator-diff1.png -------------------------------------------------------------------------------- /.github/other/comparator-diff2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/comparator-diff2.png -------------------------------------------------------------------------------- /.github/other/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/demo1.gif -------------------------------------------------------------------------------- /.github/other/discrepancies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/discrepancies.png -------------------------------------------------------------------------------- /.github/other/gif_comparator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/gif_comparator.gif -------------------------------------------------------------------------------- /.github/other/gitlab-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/.github/other/gitlab-support.png -------------------------------------------------------------------------------- /.github/workflows/codeql.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: "0 0 * * 1" 22 | 23 | permissions: 24 | contents: read 25 | 26 | jobs: 27 | analyze: 28 | name: Analyze 29 | runs-on: ubuntu-latest 30 | permissions: 31 | actions: read 32 | contents: read 33 | security-events: write 34 | 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | language: ["typescript"] 39 | # CodeQL supports [ $supported-codeql-languages ] 40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 41 | 42 | steps: 43 | - name: Harden Runner 44 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 45 | with: 46 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 47 | 48 | - name: Checkout repository 49 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 50 | 51 | # Initializes the CodeQL tools for scanning. 52 | - name: Initialize CodeQL 53 | uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 54 | with: 55 | languages: ${{ matrix.language }} 56 | # If you wish to specify custom queries, you can do so here or in a config file. 57 | # By default, queries listed here will override any specified in a config file. 58 | # Prefix the list here with "+" to use these queries and those in the config file. 59 | 60 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 61 | # If this step fails, then you should remove it and run the build manually (see below) 62 | - name: Autobuild 63 | uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 64 | 65 | # ℹ️ Command-line programs to run using the OS shell. 66 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 67 | 68 | # If the Autobuild fails above, remove it and uncomment the following three lines. 69 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 70 | 71 | # - run: | 72 | # echo "Run, Build Application using script" 73 | # ./location_of_script_within_repo/buildscript.sh 74 | 75 | - name: Perform CodeQL Analysis 76 | uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 77 | with: 78 | category: "/language:${{matrix.language}}" 79 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: "Dependency Review" 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 23 | 24 | - name: "Checkout Repository" 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: "Dependency Review" 27 | uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0 28 | -------------------------------------------------------------------------------- /.github/workflows/deployment.yml: -------------------------------------------------------------------------------- 1 | name: deployment 2 | on: 3 | push: 4 | branches: 5 | - main 6 | permissions: 7 | contents: write 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Harden Runner 13 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 14 | with: 15 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 16 | 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - name: installing node@18 19 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 20 | with: 21 | node-version: 20.9.0 22 | - name: installing dependencies 23 | run: npm ci 24 | # - name: test 25 | # run: npm run test 26 | - name: build 27 | run: npm run build 28 | - name: Deploy in GitHub pages 29 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./build 33 | -------------------------------------------------------------------------------- /.github/workflows/pr_check.yml: -------------------------------------------------------------------------------- 1 | name: pr check 2 | on: [pull_request] 3 | permissions: 4 | contents: read 5 | 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Harden Runner 11 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 12 | with: 13 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 14 | 15 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | - name: installing node@18 17 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 18 | with: 19 | node-version: 20.9.0 20 | - name: installing dependencies 21 | run: npm ci 22 | - name: linting 23 | run: npm run lint 24 | - name: formatting 25 | run: npm run format 26 | - name: build 27 | run: npm run build 28 | - name: unit test 29 | run: npm run test 30 | - name: prepare test e2e 31 | run: npm run start & sleep 10 32 | - name: test e2e 33 | run: npm run test:e2e-ci 34 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: "16 21 * * 1" 14 | push: 15 | branches: ["main"] 16 | workflow_dispatch: 17 | 18 | # Declare default permissions as read only. 19 | permissions: read-all 20 | 21 | jobs: 22 | analysis: 23 | name: Scorecard analysis 24 | runs-on: ubuntu-latest 25 | permissions: 26 | # Needed to upload the results to code-scanning dashboard. 27 | security-events: write 28 | # Needed to publish results and get a badge (see publish_results below). 29 | id-token: write 30 | # Uncomment the permissions below if installing in a private repository. 31 | # contents: read 32 | # actions: read 33 | 34 | steps: 35 | - name: Harden Runner 36 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 37 | with: 38 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 39 | 40 | - name: "Checkout code" 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.1.0 42 | with: 43 | persist-credentials: false 44 | 45 | - name: "Run analysis" 46 | uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 47 | with: 48 | results_file: results.sarif 49 | results_format: sarif 50 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 51 | # - you want to enable the Branch-Protection check on a *public* repository, or 52 | # - you are installing Scorecard on a *private* repository 53 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 54 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 55 | 56 | # Public repositories: 57 | # - Publish results to OpenSSF REST API for easy access by consumers 58 | # - Allows the repository to include the Scorecard badge. 59 | # - See https://github.com/ossf/scorecard-action#publishing-results. 60 | # For private repositories: 61 | # - `publish_results` will always be set to `false`, regardless 62 | # of the value entered here. 63 | publish_results: true 64 | 65 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 66 | # format to the repository Actions tab. 67 | - name: "Upload artifact" 68 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 69 | with: 70 | name: SARIF file 71 | path: results.sarif 72 | retention-days: 5 73 | 74 | # Upload the results to GitHub's code scanning dashboard. 75 | - name: "Upload to code-scanning" 76 | uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v2.2.6 77 | with: 78 | sarif_file: results.sarif 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,react,node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,react,node 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### macOS Patch ### 49 | # iCloud generated files 50 | *.icloud 51 | 52 | ### Node ### 53 | # Logs 54 | logs 55 | *.log 56 | npm-debug.log* 57 | yarn-debug.log* 58 | yarn-error.log* 59 | lerna-debug.log* 60 | .pnpm-debug.log* 61 | 62 | # Diagnostic reports (https://nodejs.org/api/report.html) 63 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 64 | 65 | # Runtime data 66 | pids 67 | *.pid 68 | *.seed 69 | *.pid.lock 70 | 71 | # Directory for instrumented libs generated by jscoverage/JSCover 72 | lib-cov 73 | 74 | # Coverage directory used by tools like istanbul 75 | coverage 76 | *.lcov 77 | 78 | # nyc test coverage 79 | .nyc_output 80 | 81 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 82 | .grunt 83 | 84 | # Bower dependency directory (https://bower.io/) 85 | bower_components 86 | 87 | # node-waf configuration 88 | .lock-wscript 89 | 90 | # Compiled binary addons (https://nodejs.org/api/addons.html) 91 | build/Release 92 | 93 | # Dependency directories 94 | node_modules/ 95 | jspm_packages/ 96 | 97 | # Snowpack dependency directory (https://snowpack.dev/) 98 | web_modules/ 99 | 100 | # TypeScript cache 101 | *.tsbuildinfo 102 | 103 | # Optional npm cache directory 104 | .npm 105 | 106 | # Optional eslint cache 107 | .eslintcache 108 | 109 | # Optional stylelint cache 110 | .stylelintcache 111 | 112 | # Microbundle cache 113 | .rpt2_cache/ 114 | .rts2_cache_cjs/ 115 | .rts2_cache_es/ 116 | .rts2_cache_umd/ 117 | 118 | # Optional REPL history 119 | .node_repl_history 120 | 121 | # Output of 'npm pack' 122 | *.tgz 123 | 124 | # Yarn Integrity file 125 | .yarn-integrity 126 | 127 | # dotenv environment variable files 128 | .env 129 | .env.development.local 130 | .env.test.local 131 | .env.production.local 132 | .env.local 133 | 134 | # parcel-bundler cache (https://parceljs.org/) 135 | .cache 136 | .parcel-cache 137 | 138 | # Next.js build output 139 | .next 140 | out 141 | 142 | # Nuxt.js build / generate output 143 | .nuxt 144 | dist 145 | 146 | # Gatsby files 147 | .cache/ 148 | # Comment in the public line in if your project uses Gatsby and not Next.js 149 | # https://nextjs.org/blog/next-9-1#public-directory-support 150 | # public 151 | 152 | # vuepress build output 153 | .vuepress/dist 154 | 155 | # vuepress v2.x temp and cache directory 156 | .temp 157 | 158 | # Docusaurus cache and generated files 159 | .docusaurus 160 | 161 | # Serverless directories 162 | .serverless/ 163 | 164 | # FuseBox cache 165 | .fusebox/ 166 | 167 | # DynamoDB Local files 168 | .dynamodb/ 169 | 170 | # TernJS port file 171 | .tern-port 172 | 173 | # Stores VSCode versions used for testing VSCode extensions 174 | .vscode-test 175 | 176 | # yarn v2 177 | .yarn/cache 178 | .yarn/unplugged 179 | .yarn/build-state.yml 180 | .yarn/install-state.gz 181 | .pnp.* 182 | 183 | ### Node Patch ### 184 | # Serverless Webpack directories 185 | .webpack/ 186 | 187 | # Optional stylelint cache 188 | 189 | # SvelteKit build / generate output 190 | .svelte-kit 191 | 192 | ### react ### 193 | .DS_* 194 | **/*.backup.* 195 | **/*.back.* 196 | 197 | node_modules 198 | 199 | *.sublime* 200 | 201 | psd 202 | thumb 203 | sketch 204 | 205 | ### Windows ### 206 | # Windows thumbnail cache files 207 | Thumbs.db 208 | Thumbs.db:encryptable 209 | ehthumbs.db 210 | ehthumbs_vista.db 211 | 212 | # Dump file 213 | *.stackdump 214 | 215 | # Folder config file 216 | [Dd]esktop.ini 217 | 218 | # Recycle Bin used on file shares 219 | $RECYCLE.BIN/ 220 | 221 | # Windows Installer files 222 | *.cab 223 | *.msi 224 | *.msix 225 | *.msm 226 | *.msp 227 | 228 | # Windows shortcuts 229 | *.lnk 230 | 231 | # End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,react,node 232 | 233 | build -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.9.0 -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSSF Scorecard Visualizer 2 | 3 | The OpenSSF Scorecard Monitor Visualizer is a tool that provides a visual representation of the OpenSSF Scorecard data for monitoring the security status of open source projects. It fetches the scorecard data from the [OpenSSF Scorecard API](https://api.securityscorecards.dev/#/results) and presents it in a user-friendly and interactive visual format. 4 | 5 | The Visualizer is part of the [OpenSSF Scorecard Monitor](https://github.com/UlisesGascon/openssf-scorecard-monitor), where you can access to the features. 6 | 7 | ## Features 8 | 9 | ### Scorecard Data Visualizer 10 | 11 | Display the OpenSSF Scorecard data in a visual format for easy understanding and analysis. 12 | 13 |
14 |
15 | visualizer-in-action 16 |
17 |
18 | 19 | ### Scorecard Data Comparator 20 | 21 | Compare between two commits that reported Scorecard data. See how the scores changed and further details. 22 | 23 |
24 |
25 | comparator-in-action 26 |
27 |
28 | 29 | ### Scorecard Data Comparator Diff 30 | 31 | Makes easier the visualization of the differences in the Scorecard comparator reasoning and details. 32 | 33 |
34 |
35 | comparator-diff-reasoning 36 |
37 |
38 | comparator-diff-details 39 |
40 |
41 | 42 | ### Support to GitLab repositories 43 | 44 | The project provides support of visualization and diff comparation for GitLab projects. In the GitLab version, deps.dev and step security links are not included, as those platforms don't support GitLab projects yet. 45 | 46 |
47 |
48 | visualizer-for-gitlab-repos 49 |
50 |
51 | 52 | ### Discrepancies management 53 | 54 | The Scorecard API can provide discrepancies in the data while comparing between two commits due [technical reasons](https://github.com/ossf/scorecard/issues/3438). The visualizer provides a way to showcase the discrepancies found. 55 | 56 |
57 |
58 | discrepancies preview 59 |
60 |
61 | 62 | ## How to use it 63 | 64 | The Visualizer and the Comparator can be used outside the Monitor. 65 | 66 | You have 3 options of visualization. Depending on which one you want to check, you should craft your own url as: 67 | 68 | - Checks the latest Scorecard data available for a project: `https://ossf.github.io/scorecard-visualizer/#/projects/${platform}/${org}/${repo}` 69 | - Checks the Scorecard data for an specific commit (previously reported): `https://ossf.github.io/scorecard-visualizer/#/projects/${platform}/${org}/${repo}/commit/{commitHash}` 70 | - Compares two specific commits, previously reported: `https://ossf.github.io/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/{prevCommitHash}/{currentCommitHash}` 71 | 72 | > 👉 Please note that in order to retrieve data from the Scorecard API, it is necessary for organizations or repository owners to report their commits to the Scorecard. However, it's important to keep in mind that not all organizations report their commits, which may result in a `404 error` if the data is not available in the API. Please be aware that this behavior is expected and not indicative of a bug. 73 | 74 | ### Examples 75 | 76 | - [Nodejs latest repository Scorecard](https://ossf.github.io/scorecard-visualizer/#/projects/github.com/nodejs/node) 77 | - [Nodejs specific commit Scorecard](https://ossf.github.io/scorecard-visualizer/#/projects/github.com/nodejs/node/commit/da80964a3d708ef3ae42d4424034f155ad37e07d) 78 | - [Nodejs reported commits comparation](https://ossf.github.io/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/2ac5e9889aba461f5a54d320973d2574980d206b/da80964a3d708ef3ae42d4424034f155ad37e07d) 79 | 80 | ## Contributing 81 | 82 | Contributions are welcome! If you would like to contribute to the OpenSSF Scorecard Monitor Visualizer project, please follow these steps: 83 | 84 | 1. Fork the repository. 85 | 2. Create a new branch for your feature or bug fix. 86 | 3. Make your changes and ensure that the code is properly formatted. 87 | 4. Write tests to cover your changes if applicable. 88 | 5. Commit your changes and push them to your forked repository. 89 | 6. Submit a pull request to the main repository, explaining your changes and providing any relevant details. 90 | 91 | ## License 92 | 93 | This project is licensed under the [Apache License Version 2.0](LICENSE). 94 | 95 | ## Support 96 | 97 | If you encounter any issues or have questions about the OpenSSF Scorecard Monitor Visualizer, please [open an issue](https://github.com/ossf/scorecard-visualizer/issues/new) on the GitHub repository. 98 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /cypress/e2e/comparator.cy.ts: -------------------------------------------------------------------------------- 1 | describe("Comparator", () => { 2 | it("should render error when organization or repository does not exist", () => { 3 | cy.intercept( 4 | "GET", 5 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=2ac5e9889aba461f5a54d320973d2574980d206b", 6 | { 7 | statusCode: 404, 8 | }, 9 | ).as("getDataPreviousCommit"); 10 | 11 | cy.intercept( 12 | "GET", 13 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=077fd7d83d7d41695137c1af5b9be1d72250e69e", 14 | { 15 | statusCode: 404, 16 | }, 17 | ).as("getDataCurrentCommit"); 18 | 19 | cy.visit( 20 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/2ac5e9889aba461f5a54d320973d2574980d206b/077fd7d83d7d41695137c1af5b9be1d72250e69e", 21 | ); 22 | cy.wait("@getDataPreviousCommit"); // first try as react query behaves 23 | cy.wait("@getDataPreviousCommit"); // second try 24 | cy.wait("@getDataPreviousCommit"); // third try 25 | cy.wait("@getDataCurrentCommit"); // first try as react query behaves 26 | cy.wait("@getDataCurrentCommit"); // second try 27 | cy.wait("@getDataCurrentCommit"); // third try 28 | cy.get("h1", { timeout: 10000 }).should( 29 | "contain", 30 | "An error ocurred. Please refresh/try again.", 31 | ); 32 | cy.get("p", { timeout: 10000 }).should( 33 | "contain", 34 | "Please check if the org/repository/commit has been analysed by the Scorecard.", 35 | ); 36 | }); 37 | it("should render error when the previous commit hash does not exist", () => { 38 | cy.intercept( 39 | "GET", 40 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=2ac5e9889aba461f5a54d320973d2574980d206b", 41 | { 42 | statusCode: 404, 43 | }, 44 | ).as("getDataPreviousCommit"); 45 | 46 | cy.intercept( 47 | "GET", 48 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=077fd7d83d7d41695137c1af5b9be1d72250e69e", 49 | { 50 | statusCode: 200, 51 | fixture: "077fd7d83d7d41695137c1af5b9be1d72250e69e.json", 52 | }, 53 | ).as("getDataCurrentCommit"); 54 | 55 | cy.visit( 56 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/2ac5e9889aba461f5a54d320973d2574980d206b/077fd7d83d7d41695137c1af5b9be1d72250e69e", 57 | ); 58 | cy.wait("@getDataPreviousCommit"); // first try as react query behaves 59 | cy.wait("@getDataPreviousCommit"); // second try 60 | cy.wait("@getDataPreviousCommit"); // third try 61 | cy.wait("@getDataCurrentCommit"); // first try 62 | 63 | cy.get("h1", { timeout: 10000 }).should( 64 | "contain", 65 | "An error ocurred. Please refresh/try again.", 66 | ); 67 | cy.get("p", { timeout: 10000 }).should( 68 | "contain", 69 | "Please check if the org/repository/commit has been analysed by the Scorecard.", 70 | ); 71 | }); 72 | it("should render error when the current commit hash does not exist", () => { 73 | cy.intercept( 74 | "GET", 75 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=2ac5e9889aba461f5a54d320973d2574980d206b", 76 | { 77 | statusCode: 200, 78 | fixture: "2ac5e9889aba461f5a54d320973d2574980d206b.json", 79 | }, 80 | ).as("getDataPreviousCommit"); 81 | 82 | cy.intercept( 83 | "GET", 84 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=077fd7d83d7d41695137c1af5b9be1d72250e69e", 85 | { 86 | statusCode: 404, 87 | }, 88 | ).as("getDataCurrentCommit"); 89 | 90 | cy.visit( 91 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/2ac5e9889aba461f5a54d320973d2574980d206b/077fd7d83d7d41695137c1af5b9be1d72250e69e", 92 | ); 93 | cy.wait("@getDataPreviousCommit"); // first try as react query behaves 94 | cy.wait("@getDataCurrentCommit"); // first try as react query behaves 95 | cy.wait("@getDataCurrentCommit"); // second try 96 | cy.wait("@getDataCurrentCommit"); // third try 97 | 98 | cy.get("h1", { timeout: 10000 }).should( 99 | "contain", 100 | "An error ocurred. Please refresh/try again.", 101 | ); 102 | cy.get("p", { timeout: 10000 }).should( 103 | "contain", 104 | "Please check if the org/repository/commit has been analysed by the Scorecard.", 105 | ); 106 | }); 107 | it.only("should compare data from each commit as expected", () => { 108 | cy.intercept( 109 | "GET", 110 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=2ac5e9889aba461f5a54d320973d2574980d206b", 111 | { 112 | statusCode: 200, 113 | fixture: "2ac5e9889aba461f5a54d320973d2574980d206b.json", 114 | }, 115 | ).as("getDataPreviousCommit"); 116 | 117 | cy.intercept( 118 | "GET", 119 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=077fd7d83d7d41695137c1af5b9be1d72250e69e", 120 | { 121 | statusCode: 200, 122 | fixture: "077fd7d83d7d41695137c1af5b9be1d72250e69e.json", 123 | }, 124 | ).as("getDataCurrentCommit"); 125 | 126 | cy.visit( 127 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/compare/2ac5e9889aba461f5a54d320973d2574980d206b/077fd7d83d7d41695137c1af5b9be1d72250e69e", 128 | ); 129 | cy.wait("@getDataPreviousCommit"); // first try as react query behaves 130 | cy.wait("@getDataCurrentCommit"); // first try as react query behaves 131 | 132 | cy.get("h1").should( 133 | "contain", 134 | "OpenSSF Scorecard comparator for nodejs/node", 135 | ); 136 | 137 | cy.get('[data-testid="current-score-and-badge"] > h2').should( 138 | "contain", 139 | "Score: 7.3/10", 140 | ); 141 | 142 | cy.get('[data-testid="current-score-and-badge"] > span') 143 | .should("contain", "Unchanged") 144 | .and("have.css", "background-color") 145 | .and("eq", "rgb(108, 117, 125)"); 146 | 147 | cy.get('[data-testid="commits-analysis"]').should( 148 | "contain", 149 | "Analysis of commits (077fd7d8) and (2ac5e988)", 150 | ); 151 | 152 | cy.get('[data-testid="commits-analysis"] > a:first-child') 153 | .should("contain", "(077fd7d8)") 154 | .and("have.attr", "href") 155 | .and( 156 | "include", 157 | "https://github.com/nodejs/node/commit/077fd7d83d7d41695137c1af5b9be1d72250e69e", 158 | ); 159 | 160 | cy.get('[data-testid="commits-analysis"] > a:last-child') 161 | .should("contain", "(2ac5e988)") 162 | .and("have.attr", "href") 163 | .and( 164 | "include", 165 | "https://github.com/nodejs/node/commit/2ac5e9889aba461f5a54d320973d2574980d206b", 166 | ); 167 | 168 | // @TODO: restore when https://github.com/ossf/scorecard-visualizer/actions/runs/5457182231/jobs/9930918315?pr=106 is solved 169 | // cy.get('[data-testid="date"]').should("contain", "Date: June 8, 2023"); 170 | 171 | cy.get('[data-testid="scorecard-version"]').should( 172 | "contain", 173 | "Scorecard version v4.10.5 (27cfe92e)", 174 | ); 175 | 176 | cy.get('[data-testid="Binary-Artifacts"]').should( 177 | "contain", 178 | "Binary-Artifacts", 179 | ); 180 | cy.get('[data-testid="Binary-Artifacts-score"] > span') 181 | .should("contain", "Unchanged") 182 | .and("have.css", "background-color") 183 | .and("eq", "rgb(108, 117, 125)"); 184 | 185 | cy.get('[data-testid="Binary-Artifacts-score"] > span').should( 186 | "contain", 187 | "0/10", 188 | ); 189 | 190 | cy.get('[data-testid="Branch-Protection"]').should( 191 | "contain", 192 | "Branch-Protection", 193 | ); 194 | cy.get('[data-testid="Branch-Protection-score"] > abbr > span').should( 195 | "contain", 196 | "?", 197 | ); 198 | 199 | cy.get('[data-testid="CI-Tests"]').should("contain", "CI-Tests"); 200 | cy.get('[data-testid="CI-Tests-score"] > span') 201 | .should("contain", "Decreased -1") 202 | .and("have.css", "background-color") 203 | .and("eq", "rgb(220, 53, 69)"); 204 | cy.get('[data-testid="CI-Tests-score"] > span').and("contain", "9/10"); 205 | cy.get('[data-testid="CI-Tests"] ~ h4').should( 206 | "contain", 207 | "Additional details / variations", 208 | ); 209 | 210 | cy.get('[data-testid="CII-Best-Practices"]').should( 211 | "contain", 212 | "CII-Best-Practices", 213 | ); 214 | cy.get('[data-testid="CII-Best-Practices-score"] > span') 215 | .should("contain", "Unchanged") 216 | .and("have.css", "background-color") 217 | .and("eq", "rgb(108, 117, 125)"); 218 | cy.get('[data-testid="CII-Best-Practices-score"] > span').should( 219 | "contain", 220 | "5/10", 221 | ); 222 | 223 | cy.get('[data-testid="Code-Review"]').should("contain", "Code-Review"); 224 | cy.get('[data-testid="Code-Review-score"] > span') 225 | .should("contain", "Unchanged") 226 | .and("have.css", "background-color") 227 | .and("eq", "rgb(108, 117, 125)"); 228 | cy.get('[data-testid="Code-Review-score"] > span').should( 229 | "contain", 230 | "0/10", 231 | ); 232 | cy.get('[data-testid="Code-Review"] ~ h4').should( 233 | "contain", 234 | "Additional details / variations", 235 | ); 236 | 237 | cy.get('[data-testid="Contributors"]').should("contain", "Contributors"); 238 | cy.get('[data-testid="Contributors-score"] > span') 239 | .should("contain", "Unchanged") 240 | .and("have.css", "background-color") 241 | .and("eq", "rgb(108, 117, 125)"); 242 | cy.get('[data-testid="Contributors-score"] > span').should( 243 | "contain", 244 | "10/10", 245 | ); 246 | 247 | cy.get('[data-testid="Dangerous-Workflow"]').should( 248 | "contain", 249 | "Dangerous-Workflow", 250 | ); 251 | cy.get('[data-testid="Dangerous-Workflow-score"] > span') 252 | .should("contain", "Unchanged") 253 | .and("have.css", "background-color") 254 | .and("eq", "rgb(108, 117, 125)"); 255 | cy.get('[data-testid="Dangerous-Workflow-score"] > span').should( 256 | "contain", 257 | "10/10", 258 | ); 259 | 260 | cy.get('[data-testid="Dependency-Update-Tool"]').should( 261 | "contain", 262 | "Dependency-Update-Tool", 263 | ); 264 | cy.get('[data-testid="Dependency-Update-Tool-score"] > span') 265 | .should("contain", "Unchanged") 266 | .and("have.css", "background-color") 267 | .and("eq", "rgb(108, 117, 125)"); 268 | cy.get('[data-testid="Dependency-Update-Tool-score"] > span').should( 269 | "contain", 270 | "10/10", 271 | ); 272 | 273 | cy.get('[data-testid="Fuzzing"]').should("contain", "Fuzzing"); 274 | cy.get('[data-testid="Fuzzing-score"] > span') 275 | .should("contain", "Unchanged") 276 | .and("have.css", "background-color") 277 | .and("eq", "rgb(108, 117, 125)"); 278 | cy.get('[data-testid="Fuzzing-score"] > span').should("contain", "10/10"); 279 | 280 | cy.get('[data-testid="License"]').should("contain", "License"); 281 | cy.get('[data-testid="License-score"] > span') 282 | .should("contain", "Unchanged") 283 | .and("have.css", "background-color") 284 | .and("eq", "rgb(108, 117, 125)"); 285 | cy.get('[data-testid="License-score"] > span').should("contain", "9/10"); 286 | 287 | cy.get('[data-testid="Maintained"]').should("contain", "Maintained"); 288 | cy.get('[data-testid="Maintained-score"] > span') 289 | .should("contain", "Unchanged") 290 | .and("have.css", "background-color") 291 | .and("eq", "rgb(108, 117, 125)"); 292 | cy.get('[data-testid="Maintained-score"] > span').should( 293 | "contain", 294 | "10/10", 295 | ); 296 | cy.get('[data-testid="Maintained"] ~ h4').should( 297 | "contain", 298 | "Additional details / variations", 299 | ); 300 | 301 | cy.get('[data-testid="Packaging"]').should("contain", "Packaging"); 302 | cy.get('[data-testid="Packaging-score"] > abbr > span').should( 303 | "contain", 304 | "?", 305 | ); 306 | 307 | cy.get('[data-testid="Pinned-Dependencies"]').should( 308 | "contain", 309 | "Pinned-Dependencies", 310 | ); 311 | cy.get('[data-testid="Pinned-Dependencies-score"] > span') 312 | .should("contain", "Unchanged") 313 | .and("have.css", "background-color") 314 | .and("eq", "rgb(108, 117, 125)"); 315 | cy.get('[data-testid="Pinned-Dependencies-score"] > span').should( 316 | "contain", 317 | "7/10", 318 | ); 319 | cy.get('[data-testid="Pinned-Dependencies"] ~ h4').should( 320 | "contain", 321 | "Additional details / variations", 322 | ); 323 | 324 | cy.get('[data-testid="SAST"]').should("contain", "SAST"); 325 | cy.get('[data-testid="SAST-score"] > span') 326 | .should("contain", "Unchanged") 327 | .and("have.css", "background-color") 328 | .and("eq", "rgb(108, 117, 125)"); 329 | cy.get('[data-testid="SAST-score"] > span').should("contain", "0/10"); 330 | 331 | cy.get('[data-testid="Security-Policy"]').should( 332 | "contain", 333 | "Security-Policy", 334 | ); 335 | cy.get('[data-testid="Security-Policy-score"] > span') 336 | .should("contain", "Unchanged") 337 | .and("have.css", "background-color") 338 | .and("eq", "rgb(108, 117, 125)"); 339 | cy.get('[data-testid="Security-Policy-score"] > span').should( 340 | "contain", 341 | "10/10", 342 | ); 343 | 344 | cy.get('[data-testid="Signed-Releases"]').should( 345 | "contain", 346 | "Signed-Releases", 347 | ); 348 | cy.get('[data-testid="Signed-Releases-score"] > abbr > span').should( 349 | "contain", 350 | "?", 351 | ); 352 | 353 | cy.get('[data-testid="Token-Permissions"]').should( 354 | "contain", 355 | "Token-Permissions", 356 | ); 357 | cy.get('[data-testid="Token-Permissions-score"] > span') 358 | .should("contain", "Unchanged") 359 | .and("have.css", "background-color") 360 | .and("eq", "rgb(108, 117, 125)"); 361 | cy.get('[data-testid="Token-Permissions-score"] > span').should( 362 | "contain", 363 | "10/10", 364 | ); 365 | cy.get('[data-testid="Token-Permissions"] ~ h4').should( 366 | "contain", 367 | "Additional details / variations", 368 | ); 369 | 370 | cy.get('[data-testid="Vulnerabilities"]').should( 371 | "contain", 372 | "Vulnerabilities", 373 | ); 374 | cy.get('[data-testid="Vulnerabilities-score"] > span') 375 | .should("contain", "Increased 2.7") 376 | .and("have.css", "background-color") 377 | .and("eq", "rgb(24, 135, 84)"); 378 | cy.get('[data-testid="Vulnerabilities-score"] > span').should( 379 | "contain", 380 | "10/10", 381 | ); 382 | cy.get('[data-testid="Vulnerabilities"] ~ h4').should( 383 | "contain", 384 | "Additional details / variations", 385 | ); 386 | }); 387 | }); 388 | -------------------------------------------------------------------------------- /cypress/e2e/visualizer.cy.ts: -------------------------------------------------------------------------------- 1 | describe("Visualizer", () => { 2 | it("should render error when organization or repository does not exist", () => { 3 | cy.intercept( 4 | "GET", 5 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node", 6 | { 7 | statusCode: 404, 8 | }, 9 | ).as("getData"); 10 | 11 | cy.visit( 12 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node", 13 | ); 14 | cy.wait("@getData"); // first try as react query behaves 15 | cy.wait("@getData"); // second try 16 | cy.wait("@getData"); // third try 17 | cy.get("h1", { timeout: 10000 }).should( 18 | "contain", 19 | "An error ocurred. Please refresh/try again.", 20 | ); 21 | cy.get("p", { timeout: 10000 }).should( 22 | "contain", 23 | "Please check if the org/repository/commit has been analysed by the Scorecard.", 24 | ); 25 | }); 26 | 27 | it("should render error when the commit hash does not exist", () => { 28 | cy.intercept( 29 | "GET", 30 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=19fa9f1bc47b0666be0747583bea8cb3d8ad5eb1", 31 | { 32 | statusCode: 404, 33 | }, 34 | ).as("getData"); 35 | 36 | cy.visit( 37 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/commit/19fa9f1bc47b0666be0747583bea8cb3d8ad5eb1", 38 | ); 39 | cy.wait("@getData"); // first try as react query behaves 40 | cy.wait("@getData"); // second try 41 | cy.wait("@getData"); // third try 42 | cy.get("h1", { timeout: 10000 }).should( 43 | "contain", 44 | "An error ocurred. Please refresh/try again.", 45 | ); 46 | cy.get("p", { timeout: 10000 }).should( 47 | "contain", 48 | "Please check if the org/repository/commit has been analysed by the Scorecard.", 49 | ); 50 | }); 51 | 52 | it("should render a valid scoredata visualization for the latest reported commit", () => { 53 | cy.intercept( 54 | "GET", 55 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node", 56 | { 57 | statusCode: 200, 58 | fixture: "50477fa35367bb76e5f56ac93d661b01a5578cec.json", 59 | }, 60 | ).as("getData"); 61 | 62 | cy.visit( 63 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node", 64 | ); 65 | cy.wait("@getData"); 66 | cy.get("h1").should("contain", "OpenSSF Scorecard for nodejs/node"); 67 | 68 | cy.get("h2").should("contain", "Score: 7.3/10"); 69 | 70 | cy.get('[data-testid="date"]').should("contain", "Date: July 2, 2023"); 71 | 72 | cy.get('[data-testid="scorecard-version"]').should( 73 | "contain", 74 | "Scorecard version v4.10.5 (27cfe92e)", 75 | ); 76 | cy.get('[data-testid="scorecard-version"] > a') 77 | .should("contain", "(27cfe92e)") 78 | .and("have.attr", "href") 79 | .and( 80 | "include", 81 | "https://github.com/ossf/scorecard/commit/27cfe92ed356fdb5a398c919ad480817ea907808", 82 | ); 83 | 84 | cy.get('[data-testid="current-commit"]').should( 85 | "contain", 86 | "Current commit (50477fa3)", 87 | ); 88 | cy.get('[data-testid="current-commit"] > a') 89 | .should("contain", "(50477fa3)") 90 | .and("have.attr", "href") 91 | .and( 92 | "include", 93 | "https://github.com/nodejs/node/commit/50477fa35367bb76e5f56ac93d661b01a5578cec", 94 | ); 95 | 96 | cy.get('[data-testid="deps-dev"]').should( 97 | "contain", 98 | "Additional info at deps.dev", 99 | ); 100 | cy.get('[data-testid="deps-dev"] > a') 101 | .should("contain", "deps.dev") 102 | .and("have.attr", "href") 103 | .and("include", "https://deps.dev/project/github/nodejs%2Fnode"); 104 | 105 | cy.get('[data-testid="step-security"]').should( 106 | "contain", 107 | "Improve your scoring with StepSecurity", 108 | ); 109 | cy.get('[data-testid="step-security"] > a') 110 | .should("contain", "StepSecurity") 111 | .and("have.attr", "href") 112 | .and( 113 | "include", 114 | "https://app.stepsecurity.io/securerepo?repo=nodejs/node", 115 | ); 116 | 117 | cy.get('[data-testid="Binary-Artifacts"]') 118 | .should("contain", "Binary-Artifacts") 119 | .and("contain", "0/10"); 120 | cy.get('[data-testid="Branch-Protection"]') 121 | .should("contain", "Branch-Protection") 122 | .and("contain", "?"); 123 | cy.get('[data-testid="CI-Tests"]') 124 | .should("contain", "CI-Tests") 125 | .and("contain", "9/10"); 126 | cy.get('[data-testid="CII-Best-Practices"]') 127 | .should("contain", "CII-Best-Practices") 128 | .and("contain", "5/10"); 129 | cy.get('[data-testid="Code-Review"]') 130 | .should("contain", "Code-Review") 131 | .and("contain", "0/10"); 132 | cy.get('[data-testid="Contributors"]') 133 | .should("contain", "Contributors") 134 | .and("contain", "10/10"); 135 | cy.get('[data-testid="Dangerous-Workflow"]') 136 | .should("contain", "Dangerous-Workflow") 137 | .and("contain", "10/10"); 138 | cy.get('[data-testid="Dependency-Update-Tool"]') 139 | .should("contain", "Dependency-Update-Tool") 140 | .and("contain", "10/10"); 141 | cy.get('[data-testid="Fuzzing"]') 142 | .should("contain", "Fuzzing") 143 | .and("contain", "10/10"); 144 | cy.get('[data-testid="License"]') 145 | .should("contain", "License") 146 | .and("contain", "9/10"); 147 | cy.get('[data-testid="Maintained"]') 148 | .should("contain", "Maintained") 149 | .and("contain", "10/10"); 150 | cy.get('[data-testid="Packaging"]') 151 | .should("contain", "Packaging") 152 | .and("contain", "?"); 153 | cy.get('[data-testid="Pinned-Dependencies"]') 154 | .should("contain", "Pinned-Dependencies") 155 | .and("contain", "7/10"); 156 | cy.get('[data-testid="SAST"]') 157 | .should("contain", "SAST") 158 | .and("contain", "0/10"); 159 | cy.get('[data-testid="Security-Policy"]') 160 | .should("contain", "Security-Policy") 161 | .and("contain", "10/10"); 162 | cy.get('[data-testid="Signed-Releases"]') 163 | .should("contain", "Signed-Releases") 164 | .and("contain", "?"); 165 | cy.get('[data-testid="Token-Permissions"]') 166 | .should("contain", "Token-Permissions") 167 | .and("contain", "10/10"); 168 | cy.get('[data-testid="Vulnerabilities"]') 169 | .should("contain", "Vulnerabilities") 170 | .and("contain", "10/10"); 171 | }); 172 | it("should render a valid scoredata visualization for an specific reported commit", () => { 173 | cy.intercept( 174 | "GET", 175 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=50477fa35367bb76e5f56ac93d661b01a5578cec", 176 | { 177 | statusCode: 200, 178 | fixture: "50477fa35367bb76e5f56ac93d661b01a5578cec.json", 179 | }, 180 | ).as("getData"); 181 | 182 | cy.visit( 183 | "localhost:3000/scorecard-visualizer/#/projects/github.com/nodejs/node/commit/50477fa35367bb76e5f56ac93d661b01a5578cec", 184 | ); 185 | cy.wait("@getData"); 186 | cy.get("h1").should("contain", "OpenSSF Scorecard for nodejs/node"); 187 | 188 | cy.get("h2").should("contain", "Score: 7.3/10"); 189 | 190 | cy.get('[data-testid="date"]').should("contain", "Date: July 2, 2023"); 191 | 192 | cy.get('[data-testid="scorecard-version"]').should( 193 | "contain", 194 | "Scorecard version v4.10.5 (27cfe92e)", 195 | ); 196 | cy.get('[data-testid="scorecard-version"] > a') 197 | .should("contain", "(27cfe92e)") 198 | .and("have.attr", "href") 199 | .and( 200 | "include", 201 | "https://github.com/ossf/scorecard/commit/27cfe92ed356fdb5a398c919ad480817ea907808", 202 | ); 203 | 204 | cy.get('[data-testid="current-commit"]').should( 205 | "contain", 206 | "Current commit (50477fa3)", 207 | ); 208 | cy.get('[data-testid="current-commit"] > a') 209 | .should("contain", "(50477fa3)") 210 | .and("have.attr", "href") 211 | .and( 212 | "include", 213 | "https://github.com/nodejs/node/commit/50477fa35367bb76e5f56ac93d661b01a5578cec", 214 | ); 215 | 216 | cy.get('[data-testid="deps-dev"]').should( 217 | "contain", 218 | "Additional info at deps.dev", 219 | ); 220 | cy.get('[data-testid="deps-dev"] > a') 221 | .should("contain", "deps.dev") 222 | .and("have.attr", "href") 223 | .and("include", "https://deps.dev/project/github/nodejs%2Fnode"); 224 | 225 | cy.get('[data-testid="step-security"]').should( 226 | "contain", 227 | "Improve your scoring with StepSecurity", 228 | ); 229 | cy.get('[data-testid="step-security"] > a') 230 | .should("contain", "StepSecurity") 231 | .and("have.attr", "href") 232 | .and( 233 | "include", 234 | "https://app.stepsecurity.io/securerepo?repo=nodejs/node", 235 | ); 236 | 237 | cy.get('[data-testid="Binary-Artifacts"]') 238 | .should("contain", "Binary-Artifacts") 239 | .and("contain", "0/10"); 240 | cy.get('[data-testid="Branch-Protection"]') 241 | .should("contain", "Branch-Protection") 242 | .and("contain", "?"); 243 | cy.get('[data-testid="CI-Tests"]') 244 | .should("contain", "CI-Tests") 245 | .and("contain", "9/10"); 246 | cy.get('[data-testid="CII-Best-Practices"]') 247 | .should("contain", "CII-Best-Practices") 248 | .and("contain", "5/10"); 249 | cy.get('[data-testid="Code-Review"]') 250 | .should("contain", "Code-Review") 251 | .and("contain", "0/10"); 252 | cy.get('[data-testid="Contributors"]') 253 | .should("contain", "Contributors") 254 | .and("contain", "10/10"); 255 | cy.get('[data-testid="Dangerous-Workflow"]') 256 | .should("contain", "Dangerous-Workflow") 257 | .and("contain", "10/10"); 258 | cy.get('[data-testid="Dependency-Update-Tool"]') 259 | .should("contain", "Dependency-Update-Tool") 260 | .and("contain", "10/10"); 261 | cy.get('[data-testid="Fuzzing"]') 262 | .should("contain", "Fuzzing") 263 | .and("contain", "10/10"); 264 | cy.get('[data-testid="License"]') 265 | .should("contain", "License") 266 | .and("contain", "9/10"); 267 | cy.get('[data-testid="Maintained"]') 268 | .should("contain", "Maintained") 269 | .and("contain", "10/10"); 270 | cy.get('[data-testid="Packaging"]') 271 | .should("contain", "Packaging") 272 | .and("contain", "?"); 273 | cy.get('[data-testid="Pinned-Dependencies"]') 274 | .should("contain", "Pinned-Dependencies") 275 | .and("contain", "7/10"); 276 | cy.get('[data-testid="SAST"]') 277 | .should("contain", "SAST") 278 | .and("contain", "0/10"); 279 | cy.get('[data-testid="Security-Policy"]') 280 | .should("contain", "Security-Policy") 281 | .and("contain", "10/10"); 282 | cy.get('[data-testid="Signed-Releases"]') 283 | .should("contain", "Signed-Releases") 284 | .and("contain", "?"); 285 | cy.get('[data-testid="Token-Permissions"]') 286 | .should("contain", "Token-Permissions") 287 | .and("contain", "10/10"); 288 | cy.get('[data-testid="Vulnerabilities"]') 289 | .should("contain", "Vulnerabilities") 290 | .and("contain", "10/10"); 291 | }); 292 | }); 293 | -------------------------------------------------------------------------------- /cypress/fixtures/077fd7d83d7d41695137c1af5b9be1d72250e69e.json: -------------------------------------------------------------------------------- 1 | { 2 | "date": "2023-06-07T23:32:59Z", 3 | "repo": { 4 | "name": "github.com/nodejs/node", 5 | "commit": "077fd7d83d7d41695137c1af5b9be1d72250e69e" 6 | }, 7 | "scorecard": { 8 | "version": "v4.10.5", 9 | "commit": "27cfe92ed356fdb5a398c919ad480817ea907808" 10 | }, 11 | "score": 7.3, 12 | "checks": [ 13 | { 14 | "name": "Binary-Artifacts", 15 | "score": 0, 16 | "reason": "binaries present in source code", 17 | "details": [ 18 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp.wasm:1", 19 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp_simd.wasm:1", 20 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1115280.wasm:1", 21 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1127717.wasm:1", 22 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1191853.wasm:1", 23 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1404619.wasm:1", 24 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1115431.wasm:1", 25 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1405322.wasm:1", 26 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regression-761784.wasm:1", 27 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/valid.wasm:1", 28 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/empty_module:1", 29 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1334577:1", 30 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1335023:1", 31 | "Warn: binary detected: deps/v8/test/mjsunit/wasm/incrementer.wasm:1", 32 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86/ittfortran.o:1", 33 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86_64/ittfortran.o:1", 34 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/callback.wasm:1", 35 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/finalize.wasm:1", 36 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/global.wasm:1", 37 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hello.wasm:1", 38 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hostref.wasm:1", 39 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/memory.wasm:1", 40 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/multi.wasm:1", 41 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/reflect.wasm:1", 42 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/serialize.wasm:1", 43 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/start.wasm:1", 44 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/table.wasm:1", 45 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/threads.wasm:1", 46 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/trap.wasm:1", 47 | "Warn: binary detected: deps/zlib/google/test/data/evil_via_invalid_utf8.zip:1", 48 | "Warn: binary detected: test/fixtures/crash.wasm:1", 49 | "Warn: binary detected: test/fixtures/es-modules/simple.wasm:1", 50 | "Warn: binary detected: test/fixtures/shared-memory.wasm:1", 51 | "Warn: binary detected: test/fixtures/simple.wasm:1", 52 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm:1", 53 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm:1", 54 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm:1", 55 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm:1", 56 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm:1", 57 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm:1", 58 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm:1", 59 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm:1", 60 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm:1", 61 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm:1", 62 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm:1", 63 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm:1", 64 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm:1", 65 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm:1", 66 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm:1", 67 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm:1", 68 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm:1", 69 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm:1", 70 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm:1", 71 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm:1", 72 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm:1", 73 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm:1", 74 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm:1", 75 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm:1", 76 | "Warn: binary detected: test/wasi/wasm/cant_dotdot.wasm:1", 77 | "Warn: binary detected: test/wasi/wasm/clock_getres.wasm:1", 78 | "Warn: binary detected: test/wasi/wasm/create_symlink.wasm:1", 79 | "Warn: binary detected: test/wasi/wasm/exitcode.wasm:1", 80 | "Warn: binary detected: test/wasi/wasm/fd_prestat_get_refresh.wasm:1", 81 | "Warn: binary detected: test/wasi/wasm/follow_symlink.wasm:1", 82 | "Warn: binary detected: test/wasi/wasm/freopen.wasm:1", 83 | "Warn: binary detected: test/wasi/wasm/ftruncate.wasm:1", 84 | "Warn: binary detected: test/wasi/wasm/getentropy.wasm:1", 85 | "Warn: binary detected: test/wasi/wasm/getrusage.wasm:1", 86 | "Warn: binary detected: test/wasi/wasm/gettimeofday.wasm:1", 87 | "Warn: binary detected: test/wasi/wasm/link.wasm:1", 88 | "Warn: binary detected: test/wasi/wasm/main_args.wasm:1", 89 | "Warn: binary detected: test/wasi/wasm/notdir.wasm:1", 90 | "Warn: binary detected: test/wasi/wasm/poll.wasm:1", 91 | "Warn: binary detected: test/wasi/wasm/preopen_populates.wasm:1", 92 | "Warn: binary detected: test/wasi/wasm/read_file.wasm:1", 93 | "Warn: binary detected: test/wasi/wasm/read_file_twice.wasm:1", 94 | "Warn: binary detected: test/wasi/wasm/readdir.wasm:1", 95 | "Warn: binary detected: test/wasi/wasm/sock.wasm:1", 96 | "Warn: binary detected: test/wasi/wasm/stat.wasm:1", 97 | "Warn: binary detected: test/wasi/wasm/stdin.wasm:1", 98 | "Warn: binary detected: test/wasi/wasm/symlink_escape.wasm:1", 99 | "Warn: binary detected: test/wasi/wasm/symlink_loop.wasm:1", 100 | "Warn: binary detected: test/wasi/wasm/write_file.wasm:1" 101 | ], 102 | "documentation": { 103 | "short": "Determines if the project has generated executable (binary) artifacts in the source repository.", 104 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#binary-artifacts" 105 | } 106 | }, 107 | { 108 | "name": "Branch-Protection", 109 | "score": -1, 110 | "reason": "internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration", 111 | "details": null, 112 | "documentation": { 113 | "short": "Determines if the default and release branches are protected with GitHub's branch protection settings.", 114 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#branch-protection" 115 | } 116 | }, 117 | { 118 | "name": "CI-Tests", 119 | "score": 9, 120 | "reason": "21 out of 22 merged PRs checked by a CI test -- score normalized to 9", 121 | "details": null, 122 | "documentation": { 123 | "short": "Determines if the project runs tests before pull requests are merged.", 124 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#ci-tests" 125 | } 126 | }, 127 | { 128 | "name": "CII-Best-Practices", 129 | "score": 5, 130 | "reason": "badge detected: passing", 131 | "details": null, 132 | "documentation": { 133 | "short": "Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.", 134 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#cii-best-practices" 135 | } 136 | }, 137 | { 138 | "name": "Code-Review", 139 | "score": 0, 140 | "reason": "found 8 unreviewed human changesets", 141 | "details": null, 142 | "documentation": { 143 | "short": "Determines if the project requires code review before pull requests (aka merge requests) are merged.", 144 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#code-review" 145 | } 146 | }, 147 | { 148 | "name": "Contributors", 149 | "score": 10, 150 | "reason": "100 different organizations found -- score normalized to 10", 151 | "details": [ 152 | "Info: contributors work for CasparCG,CloudNativeJS,DxWnd,ES-Community,Empeeric,Jasig,Level,MaibornWolff,NodeRedis,NodeSummit,Old-Stuff,RuntimeTools,WebAssembly,WebAudio,activitystreams,adonisjs,ataraxia consulting,babel,browserify,cheminfo,cheminfo-js,cloudflare,dancejs,denoland,digitalocean,dimsumjs,dot-org,drogue-iot,ender-js,filecoin-project,github,github-beta,gypified,image-js,ipfs,ipld,istanbuljs,jQuerySF,js-js,jstat,jstime,libuv,linuxfoundationorg,maia-tool,maibornwolff gmbh,meatspaces,minibuf,mljs,mongodb,monome-community,multiformats,netty,node-forward,node-inspector,node4good,nodebots,nodejs,nodeshift,nodeshift-starters,nodesource,nubjs,nujs,nxtedition,oftc,openjs-foundation,peerlinks,pkgjs,polyhack,postmates,primus,propelml,red hat,redhat-developer,redhatofficial,reportico,require.io,requireio,revisitors,sagemath,sclorg,signalapp,socket supply co.,strongloop,strongloop-community,strongloop-forks,taphub,tapjs,tc39,tc39-transfer,tier.run,tu wien,ucsf-ckm,unshiftio,wafflejs,wasm-signatures,websockets,whatwg,wintercg,zakodium,zakodium-oss" 153 | ], 154 | "documentation": { 155 | "short": "Determines if the project has a set of contributors from multiple organizations (e.g., companies).", 156 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#contributors" 157 | } 158 | }, 159 | { 160 | "name": "Dangerous-Workflow", 161 | "score": 10, 162 | "reason": "no dangerous workflow patterns detected", 163 | "details": null, 164 | "documentation": { 165 | "short": "Determines if the project's GitHub Action workflows avoid dangerous patterns.", 166 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dangerous-workflow" 167 | } 168 | }, 169 | { 170 | "name": "Dependency-Update-Tool", 171 | "score": 10, 172 | "reason": "update tool detected", 173 | "details": ["Info: Dependabot detected: .github/dependabot.yml:1"], 174 | "documentation": { 175 | "short": "Determines if the project uses a dependency update tool.", 176 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dependency-update-tool" 177 | } 178 | }, 179 | { 180 | "name": "Fuzzing", 181 | "score": 10, 182 | "reason": "project is fuzzed with [OSSFuzz]", 183 | "details": null, 184 | "documentation": { 185 | "short": "Determines if the project uses fuzzing.", 186 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#fuzzing" 187 | } 188 | }, 189 | { 190 | "name": "License", 191 | "score": 9, 192 | "reason": "license file detected", 193 | "details": [ 194 | "Info: License file found in expected location: LICENSE:1", 195 | "Warn: Any licence detected not an FSF or OSI recognized license: LICENSE:1" 196 | ], 197 | "documentation": { 198 | "short": "Determines if the project has defined a license.", 199 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#license" 200 | } 201 | }, 202 | { 203 | "name": "Maintained", 204 | "score": 10, 205 | "reason": "30 commit(s) out of 30 and 17 issue activity out of 30 found in the last 90 days -- score normalized to 10", 206 | "details": null, 207 | "documentation": { 208 | "short": "Determines if the project is \"actively maintained\".", 209 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#maintained" 210 | } 211 | }, 212 | { 213 | "name": "Packaging", 214 | "score": -1, 215 | "reason": "no published package detected", 216 | "details": ["Warn: no GitHub publishing workflow detected"], 217 | "documentation": { 218 | "short": "Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.", 219 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#packaging" 220 | } 221 | }, 222 | { 223 | "name": "Pinned-Dependencies", 224 | "score": 7, 225 | "reason": "dependency not pinned by hash detected -- score normalized to 7", 226 | "details": [ 227 | "Warn: containerImage not pinned by hash: .devcontainer/Dockerfile:1: pin your Docker image by updating nodejs/devcontainer:nightly to nodejs/devcontainer:nightly@sha256:f8db157a30680ecabe44e63e54cb2ff44e1d981fef9a808dbd9b7309d5a2d301", 228 | "Warn: containerImage not pinned by hash: deps/openssl/config/Dockerfile:1: pin your Docker image by updating ubuntu:20.04 to ubuntu:20.04@sha256:db8bf6f4fb351aa7a26e27ba2686cf35a6a409f65603e59d4c203e58387dc6b3", 229 | "Warn: npmCommand not pinned by hash: .github/workflows/auto-start-ci.yml:60", 230 | "Warn: npmCommand not pinned by hash: .github/workflows/commit-queue.yml:79", 231 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux-without-intl.yml:51", 232 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux.yml:51", 233 | "Warn: npmCommand not pinned by hash: .github/workflows/daily-wpt-fyi.yml:113", 234 | "Warn: npmCommand not pinned by hash: .github/workflows/update-v8.yml:38", 235 | "Info: GitHub-owned GitHubActions are pinned", 236 | "Info: Third-party GitHubActions are pinned", 237 | "Info: no insecure (not pinned by hash) dependency downloads found in Dockerfiles", 238 | "Info: no insecure (not pinned by hash) dependency downloads found in shell scripts" 239 | ], 240 | "documentation": { 241 | "short": "Determines if the project has declared and pinned the dependencies of its build process.", 242 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#pinned-dependencies" 243 | } 244 | }, 245 | { 246 | "name": "SAST", 247 | "score": 0, 248 | "reason": "SAST tool is not run on all commits -- score normalized to 0", 249 | "details": [ 250 | "Warn: 0 commits out of 22 are checked with a SAST tool", 251 | "Warn: CodeQL tool not detected" 252 | ], 253 | "documentation": { 254 | "short": "Determines if the project uses static code analysis.", 255 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#sast" 256 | } 257 | }, 258 | { 259 | "name": "Security-Policy", 260 | "score": 10, 261 | "reason": "security policy file detected", 262 | "details": [ 263 | "Info: Found linked content in security policy: SECURITY.md", 264 | "Info: Found text in security policy: SECURITY.md", 265 | "Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md", 266 | "Info: security policy detected in current repo: SECURITY.md" 267 | ], 268 | "documentation": { 269 | "short": "Determines if the project has published a security policy.", 270 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#security-policy" 271 | } 272 | }, 273 | { 274 | "name": "Signed-Releases", 275 | "score": -1, 276 | "reason": "no releases found", 277 | "details": ["Warn: no GitHub releases found"], 278 | "documentation": { 279 | "short": "Determines if the project cryptographically signs release artifacts.", 280 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#signed-releases" 281 | } 282 | }, 283 | { 284 | "name": "Token-Permissions", 285 | "score": 10, 286 | "reason": "tokens are read-only in GitHub workflows", 287 | "details": [ 288 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:18", 289 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/auto-start-ci.yml:23", 290 | "Info: Medium severity: jobLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:43", 291 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-tarball.yml:35", 292 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-windows.yml:30", 293 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stale-feature-requests.yml:32", 294 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stale-pull-requests.yml:29", 295 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stalled.yml:13", 296 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/comment-labeled.yml:19", 297 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-lint.yml:9", 298 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-queue.yml:22", 299 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/commit-queue.yml:27", 300 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux-without-intl.yml:33", 301 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux.yml:33", 302 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-windows.yml:35", 303 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily-wpt-fyi.yml:24", 304 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily.yml:12", 305 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/doc.yml:20", 306 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-collaborators.yml:14", 307 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-tsc.yml:14", 308 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-flaky-test-issue.yml:8", 309 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-pr.yml:8", 310 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/license-builder.yml:10", 311 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/license-builder.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 312 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/linters.yml:21", 313 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/notify-on-push.yml:8", 314 | "Info: High severity: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:19", 315 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-asan.yml:37", 316 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-internet.yml:28", 317 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-linux.yml:30", 318 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-macos.yml:36", 319 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/timezone-update.yml:10", 320 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/timezone-update.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 321 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/tools.yml:47", 322 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-openssl.yml:10", 323 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-v8.yml:12" 324 | ], 325 | "documentation": { 326 | "short": "Determines if the project's workflows follow the principle of least privilege.", 327 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#token-permissions" 328 | } 329 | }, 330 | { 331 | "name": "Vulnerabilities", 332 | "score": 10, 333 | "reason": "no vulnerabilities detected", 334 | "details": null, 335 | "documentation": { 336 | "short": "Determines if the project has open, known unfixed vulnerabilities.", 337 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#vulnerabilities" 338 | } 339 | } 340 | ] 341 | } 342 | -------------------------------------------------------------------------------- /cypress/fixtures/2ac5e9889aba461f5a54d320973d2574980d206b.json: -------------------------------------------------------------------------------- 1 | { 2 | "date": "2023-04-26T08:57:49Z", 3 | "repo": { 4 | "name": "github.com/nodejs/node", 5 | "commit": "2ac5e9889aba461f5a54d320973d2574980d206b" 6 | }, 7 | "scorecard": { 8 | "version": "v4.10.5", 9 | "commit": "27cfe92ed356fdb5a398c919ad480817ea907808" 10 | }, 11 | "score": 7.3, 12 | "checks": [ 13 | { 14 | "name": "Binary-Artifacts", 15 | "score": 0, 16 | "reason": "binaries present in source code", 17 | "details": [ 18 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp.wasm:1", 19 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp_simd.wasm:1", 20 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1115280.wasm:1", 21 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1127717.wasm:1", 22 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1191853.wasm:1", 23 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1404619.wasm:1", 24 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1115431.wasm:1", 25 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1405322.wasm:1", 26 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regression-761784.wasm:1", 27 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/valid.wasm:1", 28 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/empty_module:1", 29 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1334577:1", 30 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1335023:1", 31 | "Warn: binary detected: deps/v8/test/mjsunit/wasm/incrementer.wasm:1", 32 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86/ittfortran.o:1", 33 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86_64/ittfortran.o:1", 34 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/callback.wasm:1", 35 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/finalize.wasm:1", 36 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/global.wasm:1", 37 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hello.wasm:1", 38 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hostref.wasm:1", 39 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/memory.wasm:1", 40 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/multi.wasm:1", 41 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/reflect.wasm:1", 42 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/serialize.wasm:1", 43 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/start.wasm:1", 44 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/table.wasm:1", 45 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/threads.wasm:1", 46 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/trap.wasm:1", 47 | "Warn: binary detected: deps/zlib/google/test/data/evil_via_invalid_utf8.zip:1", 48 | "Warn: binary detected: test/fixtures/crash.wasm:1", 49 | "Warn: binary detected: test/fixtures/es-modules/simple.wasm:1", 50 | "Warn: binary detected: test/fixtures/shared-memory.wasm:1", 51 | "Warn: binary detected: test/fixtures/simple.wasm:1", 52 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm:1", 53 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm:1", 54 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm:1", 55 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm:1", 56 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm:1", 57 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm:1", 58 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm:1", 59 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm:1", 60 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm:1", 61 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm:1", 62 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm:1", 63 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm:1", 64 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm:1", 65 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm:1", 66 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm:1", 67 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm:1", 68 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm:1", 69 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm:1", 70 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm:1", 71 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm:1", 72 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm:1", 73 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm:1", 74 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm:1", 75 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm:1", 76 | "Warn: binary detected: test/wasi/wasm/cant_dotdot.wasm:1", 77 | "Warn: binary detected: test/wasi/wasm/clock_getres.wasm:1", 78 | "Warn: binary detected: test/wasi/wasm/create_symlink.wasm:1", 79 | "Warn: binary detected: test/wasi/wasm/exitcode.wasm:1", 80 | "Warn: binary detected: test/wasi/wasm/fd_prestat_get_refresh.wasm:1", 81 | "Warn: binary detected: test/wasi/wasm/follow_symlink.wasm:1", 82 | "Warn: binary detected: test/wasi/wasm/freopen.wasm:1", 83 | "Warn: binary detected: test/wasi/wasm/ftruncate.wasm:1", 84 | "Warn: binary detected: test/wasi/wasm/getentropy.wasm:1", 85 | "Warn: binary detected: test/wasi/wasm/getrusage.wasm:1", 86 | "Warn: binary detected: test/wasi/wasm/gettimeofday.wasm:1", 87 | "Warn: binary detected: test/wasi/wasm/link.wasm:1", 88 | "Warn: binary detected: test/wasi/wasm/main_args.wasm:1", 89 | "Warn: binary detected: test/wasi/wasm/notdir.wasm:1", 90 | "Warn: binary detected: test/wasi/wasm/poll.wasm:1", 91 | "Warn: binary detected: test/wasi/wasm/preopen_populates.wasm:1", 92 | "Warn: binary detected: test/wasi/wasm/read_file.wasm:1", 93 | "Warn: binary detected: test/wasi/wasm/read_file_twice.wasm:1", 94 | "Warn: binary detected: test/wasi/wasm/readdir.wasm:1", 95 | "Warn: binary detected: test/wasi/wasm/sock.wasm:1", 96 | "Warn: binary detected: test/wasi/wasm/stat.wasm:1", 97 | "Warn: binary detected: test/wasi/wasm/stdin.wasm:1", 98 | "Warn: binary detected: test/wasi/wasm/symlink_escape.wasm:1", 99 | "Warn: binary detected: test/wasi/wasm/symlink_loop.wasm:1", 100 | "Warn: binary detected: test/wasi/wasm/write_file.wasm:1" 101 | ], 102 | "documentation": { 103 | "short": "Determines if the project has generated executable (binary) artifacts in the source repository.", 104 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#binary-artifacts" 105 | } 106 | }, 107 | { 108 | "name": "Branch-Protection", 109 | "score": -1, 110 | "reason": "internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration", 111 | "details": null, 112 | "documentation": { 113 | "short": "Determines if the default and release branches are protected with GitHub's branch protection settings.", 114 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#branch-protection" 115 | } 116 | }, 117 | { 118 | "name": "CI-Tests", 119 | "score": 10, 120 | "reason": "25 out of 25 merged PRs checked by a CI test -- score normalized to 10", 121 | "details": null, 122 | "documentation": { 123 | "short": "Determines if the project runs tests before pull requests are merged.", 124 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#ci-tests" 125 | } 126 | }, 127 | { 128 | "name": "CII-Best-Practices", 129 | "score": 5, 130 | "reason": "badge detected: passing", 131 | "details": null, 132 | "documentation": { 133 | "short": "Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.", 134 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#cii-best-practices" 135 | } 136 | }, 137 | { 138 | "name": "Code-Review", 139 | "score": 0, 140 | "reason": "found 5 unreviewed human changesets", 141 | "details": null, 142 | "documentation": { 143 | "short": "Determines if the project requires code review before pull requests (aka merge requests) are merged.", 144 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#code-review" 145 | } 146 | }, 147 | { 148 | "name": "Contributors", 149 | "score": 10, 150 | "reason": "100 different organizations found -- score normalized to 10", 151 | "details": [ 152 | "Info: contributors work for CasparCG,CloudNativeJS,DxWnd,ES-Community,Empeeric,Jasig,Level,MaibornWolff,NodeRedis,NodeSummit,Old-Stuff,RuntimeTools,WebAssembly,WebAudio,activitystreams,adonisjs,ataraxia consulting,babel,browserify,cheminfo,cheminfo-js,cloudflare,dancejs,denoland,digitalocean,dimsumjs,dot-org,drogue-iot,ender-js,filecoin-project,github,github-beta,gypified,image-js,ipfs,ipld,istanbuljs,jQuerySF,js-js,jstat,jstime,libuv,linuxfoundationorg,maia-tool,maibornwolff gmbh,meatspaces,minibuf,mljs,mongodb,monome-community,multiformats,netty,node-forward,node-inspector,node4good,nodebots,nodejs,nodeshift,nodeshift-starters,nodesource,nubjs,nujs,nxtedition,oftc,openjs-foundation,peerlinks,pkgjs,polyhack,postmates,primus,propelml,red hat,redhat-developer,redhatofficial,reportico,require.io,requireio,revisitors,sagemath,sclorg,signalapp,socket supply co.,strongloop,strongloop-community,strongloop-forks,taphub,tapjs,tc39,tc39-transfer,tier.run,tu wien,ucsf-ckm,unshiftio,wafflejs,wasm-signatures,websockets,whatwg,wintercg,zakodium,zakodium-oss" 153 | ], 154 | "documentation": { 155 | "short": "Determines if the project has a set of contributors from multiple organizations (e.g., companies).", 156 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#contributors" 157 | } 158 | }, 159 | { 160 | "name": "Dangerous-Workflow", 161 | "score": 10, 162 | "reason": "no dangerous workflow patterns detected", 163 | "details": null, 164 | "documentation": { 165 | "short": "Determines if the project's GitHub Action workflows avoid dangerous patterns.", 166 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dangerous-workflow" 167 | } 168 | }, 169 | { 170 | "name": "Dependency-Update-Tool", 171 | "score": 10, 172 | "reason": "update tool detected", 173 | "details": ["Info: Dependabot detected: .github/dependabot.yml:1"], 174 | "documentation": { 175 | "short": "Determines if the project uses a dependency update tool.", 176 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dependency-update-tool" 177 | } 178 | }, 179 | { 180 | "name": "Fuzzing", 181 | "score": 10, 182 | "reason": "project is fuzzed with [OSSFuzz]", 183 | "details": null, 184 | "documentation": { 185 | "short": "Determines if the project uses fuzzing.", 186 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#fuzzing" 187 | } 188 | }, 189 | { 190 | "name": "License", 191 | "score": 9, 192 | "reason": "license file detected", 193 | "details": [ 194 | "Info: License file found in expected location: LICENSE:1", 195 | "Warn: Any licence detected not an FSF or OSI recognized license: LICENSE:1" 196 | ], 197 | "documentation": { 198 | "short": "Determines if the project has defined a license.", 199 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#license" 200 | } 201 | }, 202 | { 203 | "name": "Maintained", 204 | "score": 10, 205 | "reason": "30 commit(s) out of 30 and 22 issue activity out of 30 found in the last 90 days -- score normalized to 10", 206 | "details": null, 207 | "documentation": { 208 | "short": "Determines if the project is \"actively maintained\".", 209 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#maintained" 210 | } 211 | }, 212 | { 213 | "name": "Packaging", 214 | "score": -1, 215 | "reason": "no published package detected", 216 | "details": ["Warn: no GitHub publishing workflow detected"], 217 | "documentation": { 218 | "short": "Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.", 219 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#packaging" 220 | } 221 | }, 222 | { 223 | "name": "Pinned-Dependencies", 224 | "score": 7, 225 | "reason": "dependency not pinned by hash detected -- score normalized to 7", 226 | "details": [ 227 | "Warn: containerImage not pinned by hash: .devcontainer/Dockerfile:1: pin your Docker image by updating nodejs/devcontainer:nightly to nodejs/devcontainer:nightly@sha256:b082deabe2c2b16718f277613be2d3a0aff4f83ffdbeb5cf8728b8cb1cfe81ad", 228 | "Warn: containerImage not pinned by hash: deps/openssl/config/Dockerfile:1: pin your Docker image by updating ubuntu:20.04 to ubuntu:20.04@sha256:db8bf6f4fb351aa7a26e27ba2686cf35a6a409f65603e59d4c203e58387dc6b3", 229 | "Warn: npmCommand not pinned by hash: .github/workflows/auto-start-ci.yml:60", 230 | "Warn: npmCommand not pinned by hash: .github/workflows/commit-queue.yml:79", 231 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux-without-intl.yml:51", 232 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux.yml:51", 233 | "Warn: npmCommand not pinned by hash: .github/workflows/daily-wpt-fyi.yml:113", 234 | "Warn: npmCommand not pinned by hash: .github/workflows/update-v8.yml:34", 235 | "Info: GitHub-owned GitHubActions are pinned", 236 | "Info: Third-party GitHubActions are pinned", 237 | "Info: no insecure (not pinned by hash) dependency downloads found in Dockerfiles", 238 | "Info: no insecure (not pinned by hash) dependency downloads found in shell scripts" 239 | ], 240 | "documentation": { 241 | "short": "Determines if the project has declared and pinned the dependencies of its build process.", 242 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#pinned-dependencies" 243 | } 244 | }, 245 | { 246 | "name": "SAST", 247 | "score": 0, 248 | "reason": "SAST tool is not run on all commits -- score normalized to 0", 249 | "details": [ 250 | "Warn: 0 commits out of 25 are checked with a SAST tool", 251 | "Warn: CodeQL tool not detected" 252 | ], 253 | "documentation": { 254 | "short": "Determines if the project uses static code analysis.", 255 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#sast" 256 | } 257 | }, 258 | { 259 | "name": "Security-Policy", 260 | "score": 10, 261 | "reason": "security policy file detected", 262 | "details": [ 263 | "Info: Found linked content in security policy: SECURITY.md", 264 | "Info: Found text in security policy: SECURITY.md", 265 | "Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md", 266 | "Info: security policy detected in current repo: SECURITY.md" 267 | ], 268 | "documentation": { 269 | "short": "Determines if the project has published a security policy.", 270 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#security-policy" 271 | } 272 | }, 273 | { 274 | "name": "Signed-Releases", 275 | "score": -1, 276 | "reason": "no releases found", 277 | "details": ["Warn: no GitHub releases found"], 278 | "documentation": { 279 | "short": "Determines if the project cryptographically signs release artifacts.", 280 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#signed-releases" 281 | } 282 | }, 283 | { 284 | "name": "Token-Permissions", 285 | "score": 10, 286 | "reason": "tokens are read-only in GitHub workflows", 287 | "details": [ 288 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:18", 289 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/auto-start-ci.yml:23", 290 | "Info: Medium severity: jobLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:43", 291 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-tarball.yml:35", 292 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-windows.yml:30", 293 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stale-feature-requests.yml:32", 294 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stalled.yml:13", 295 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/comment-labeled.yml:19", 296 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-lint.yml:9", 297 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-queue.yml:22", 298 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/commit-queue.yml:27", 299 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux-without-intl.yml:33", 300 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux.yml:33", 301 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-windows.yml:35", 302 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily-wpt-fyi.yml:24", 303 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily.yml:12", 304 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/doc.yml:20", 305 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-collaborators.yml:14", 306 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-tsc.yml:14", 307 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-flaky-test-issue.yml:8", 308 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-pr.yml:8", 309 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/license-builder.yml:10", 310 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/license-builder.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 311 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/linters.yml:21", 312 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/notify-on-push.yml:8", 313 | "Info: High severity: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:19", 314 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-asan.yml:37", 315 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-internet.yml:28", 316 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-linux.yml:30", 317 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-macos.yml:36", 318 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/timezone-update.yml:10", 319 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/timezone-update.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 320 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/tools.yml:40", 321 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-openssl.yml:10", 322 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-v8.yml:12" 323 | ], 324 | "documentation": { 325 | "short": "Determines if the project's workflows follow the principle of least privilege.", 326 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#token-permissions" 327 | } 328 | }, 329 | { 330 | "name": "Vulnerabilities", 331 | "score": 7.3, 332 | "reason": "Found 1 vulnerability detected", 333 | "details": ["Found 1 vulnerabilty"], 334 | "documentation": { 335 | "short": "Determines if the project has open, known unfixed vulnerabilities.", 336 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#vulnerabilities" 337 | } 338 | } 339 | ] 340 | } 341 | -------------------------------------------------------------------------------- /cypress/fixtures/50477fa35367bb76e5f56ac93d661b01a5578cec.json: -------------------------------------------------------------------------------- 1 | { 2 | "date": "2023-07-02T05:50:25Z", 3 | "repo": { 4 | "name": "github.com/nodejs/node", 5 | "commit": "50477fa35367bb76e5f56ac93d661b01a5578cec" 6 | }, 7 | "scorecard": { 8 | "version": "v4.10.5", 9 | "commit": "27cfe92ed356fdb5a398c919ad480817ea907808" 10 | }, 11 | "score": 7.3, 12 | "checks": [ 13 | { 14 | "name": "Binary-Artifacts", 15 | "score": 0, 16 | "reason": "binaries present in source code", 17 | "details": [ 18 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp.wasm:1", 19 | "Warn: binary detected: deps/undici/src/lib/llhttp/llhttp_simd.wasm:1", 20 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1115280.wasm:1", 21 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1127717.wasm:1", 22 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1191853.wasm:1", 23 | "Warn: binary detected: deps/v8/test/fuzzer/wasm/regress-1404619.wasm:1", 24 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1115431.wasm:1", 25 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regress-1405322.wasm:1", 26 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/regression-761784.wasm:1", 27 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_async/valid.wasm:1", 28 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/empty_module:1", 29 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1334577:1", 30 | "Warn: binary detected: deps/v8/test/fuzzer/wasm_streaming/regress-1335023:1", 31 | "Warn: binary detected: deps/v8/test/mjsunit/wasm/incrementer.wasm:1", 32 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86/ittfortran.o:1", 33 | "Warn: binary detected: deps/v8/third_party/ittapi/include/fortran/posix/x86_64/ittfortran.o:1", 34 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/callback.wasm:1", 35 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/finalize.wasm:1", 36 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/global.wasm:1", 37 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hello.wasm:1", 38 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/hostref.wasm:1", 39 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/memory.wasm:1", 40 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/multi.wasm:1", 41 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/reflect.wasm:1", 42 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/serialize.wasm:1", 43 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/start.wasm:1", 44 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/table.wasm:1", 45 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/threads.wasm:1", 46 | "Warn: binary detected: deps/v8/third_party/wasm-api/example/trap.wasm:1", 47 | "Warn: binary detected: deps/zlib/google/test/data/evil_via_invalid_utf8.zip:1", 48 | "Warn: binary detected: test/fixtures/crash.wasm:1", 49 | "Warn: binary detected: test/fixtures/es-modules/simple.wasm:1", 50 | "Warn: binary detected: test/fixtures/out-of-bound.wasm:1", 51 | "Warn: binary detected: test/fixtures/shared-memory.wasm:1", 52 | "Warn: binary detected: test/fixtures/simple.wasm:1", 53 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm:1", 54 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm:1", 55 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm:1", 56 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm:1", 57 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm:1", 58 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm:1", 59 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm:1", 60 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm:1", 61 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm:1", 62 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm:1", 63 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm:1", 64 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm:1", 65 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm:1", 66 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm:1", 67 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm:1", 68 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm:1", 69 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm:1", 70 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm:1", 71 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm:1", 72 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm:1", 73 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm:1", 74 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm:1", 75 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm:1", 76 | "Warn: binary detected: test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm:1", 77 | "Warn: binary detected: test/wasi/wasm/cant_dotdot.wasm:1", 78 | "Warn: binary detected: test/wasi/wasm/clock_getres.wasm:1", 79 | "Warn: binary detected: test/wasi/wasm/create_symlink.wasm:1", 80 | "Warn: binary detected: test/wasi/wasm/exitcode.wasm:1", 81 | "Warn: binary detected: test/wasi/wasm/fd_prestat_get_refresh.wasm:1", 82 | "Warn: binary detected: test/wasi/wasm/follow_symlink.wasm:1", 83 | "Warn: binary detected: test/wasi/wasm/freopen.wasm:1", 84 | "Warn: binary detected: test/wasi/wasm/ftruncate.wasm:1", 85 | "Warn: binary detected: test/wasi/wasm/getentropy.wasm:1", 86 | "Warn: binary detected: test/wasi/wasm/getrusage.wasm:1", 87 | "Warn: binary detected: test/wasi/wasm/gettimeofday.wasm:1", 88 | "Warn: binary detected: test/wasi/wasm/link.wasm:1", 89 | "Warn: binary detected: test/wasi/wasm/main_args.wasm:1", 90 | "Warn: binary detected: test/wasi/wasm/notdir.wasm:1", 91 | "Warn: binary detected: test/wasi/wasm/poll.wasm:1", 92 | "Warn: binary detected: test/wasi/wasm/preopen_populates.wasm:1", 93 | "Warn: binary detected: test/wasi/wasm/read_file.wasm:1", 94 | "Warn: binary detected: test/wasi/wasm/read_file_twice.wasm:1", 95 | "Warn: binary detected: test/wasi/wasm/readdir.wasm:1", 96 | "Warn: binary detected: test/wasi/wasm/sock.wasm:1", 97 | "Warn: binary detected: test/wasi/wasm/stat.wasm:1", 98 | "Warn: binary detected: test/wasi/wasm/stdin.wasm:1", 99 | "Warn: binary detected: test/wasi/wasm/symlink_escape.wasm:1", 100 | "Warn: binary detected: test/wasi/wasm/symlink_loop.wasm:1", 101 | "Warn: binary detected: test/wasi/wasm/write_file.wasm:1" 102 | ], 103 | "documentation": { 104 | "short": "Determines if the project has generated executable (binary) artifacts in the source repository.", 105 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#binary-artifacts" 106 | } 107 | }, 108 | { 109 | "name": "Branch-Protection", 110 | "score": -1, 111 | "reason": "internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration", 112 | "details": null, 113 | "documentation": { 114 | "short": "Determines if the default and release branches are protected with GitHub's branch protection settings.", 115 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#branch-protection" 116 | } 117 | }, 118 | { 119 | "name": "CI-Tests", 120 | "score": 9, 121 | "reason": "19 out of 20 merged PRs checked by a CI test -- score normalized to 9", 122 | "details": null, 123 | "documentation": { 124 | "short": "Determines if the project runs tests before pull requests are merged.", 125 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#ci-tests" 126 | } 127 | }, 128 | { 129 | "name": "CII-Best-Practices", 130 | "score": 5, 131 | "reason": "badge detected: passing", 132 | "details": null, 133 | "documentation": { 134 | "short": "Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.", 135 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#cii-best-practices" 136 | } 137 | }, 138 | { 139 | "name": "Code-Review", 140 | "score": 0, 141 | "reason": "found 10 unreviewed human changesets", 142 | "details": null, 143 | "documentation": { 144 | "short": "Determines if the project requires code review before pull requests (aka merge requests) are merged.", 145 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#code-review" 146 | } 147 | }, 148 | { 149 | "name": "Contributors", 150 | "score": 10, 151 | "reason": "100 different organizations found -- score normalized to 10", 152 | "details": [ 153 | "Info: contributors work for CasparCG,CloudNativeJS,DxWnd,ES-Community,Empeeric,Jasig,Level,MaibornWolff,NodeRedis,NodeSummit,Old-Stuff,RuntimeTools,WebAssembly,WebAudio,activitystreams,adonisjs,ataraxia consulting,babel,browserify,cheminfo,cheminfo-js,cloudflare,dancejs,denoland,digitalocean,dimsumjs,dot-org,drogue-iot,ender-js,filecoin-project,github,github-beta,gypified,image-js,ipfs,ipld,istanbuljs,jQuerySF,js-js,jstat,jstime,libuv,linuxfoundationorg,maia-tool,maibornwolff gmbh,meatspaces,minibuf,mljs,mongodb,monome-community,multiformats,netty,node-forward,node-inspector,node4good,nodebots,nodejs,nodeshift,nodeshift-starters,nodesource,nubjs,nujs,nxtedition,oftc,openjs-foundation,peerlinks,pkgjs,polyhack,postmates,primus,propelml,red hat,redhat-developer,redhatofficial,reportico,require.io,requireio,revisitors,sagemath,sclorg,signalapp,socket supply co.,strongloop,strongloop-community,strongloop-forks,taphub,tapjs,tc39,tc39-transfer,tier.run,tu wien,ucsf-ckm,unshiftio,wafflejs,wasm-signatures,websockets,whatwg,wintercg,zakodium,zakodium-oss" 154 | ], 155 | "documentation": { 156 | "short": "Determines if the project has a set of contributors from multiple organizations (e.g., companies).", 157 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#contributors" 158 | } 159 | }, 160 | { 161 | "name": "Dangerous-Workflow", 162 | "score": 10, 163 | "reason": "no dangerous workflow patterns detected", 164 | "details": null, 165 | "documentation": { 166 | "short": "Determines if the project's GitHub Action workflows avoid dangerous patterns.", 167 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dangerous-workflow" 168 | } 169 | }, 170 | { 171 | "name": "Dependency-Update-Tool", 172 | "score": 10, 173 | "reason": "update tool detected", 174 | "details": ["Info: Dependabot detected: .github/dependabot.yml:1"], 175 | "documentation": { 176 | "short": "Determines if the project uses a dependency update tool.", 177 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#dependency-update-tool" 178 | } 179 | }, 180 | { 181 | "name": "Fuzzing", 182 | "score": 10, 183 | "reason": "project is fuzzed with [OSSFuzz]", 184 | "details": null, 185 | "documentation": { 186 | "short": "Determines if the project uses fuzzing.", 187 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#fuzzing" 188 | } 189 | }, 190 | { 191 | "name": "License", 192 | "score": 9, 193 | "reason": "license file detected", 194 | "details": [ 195 | "Info: License file found in expected location: LICENSE:1", 196 | "Warn: Any licence detected not an FSF or OSI recognized license: LICENSE:1" 197 | ], 198 | "documentation": { 199 | "short": "Determines if the project has defined a license.", 200 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#license" 201 | } 202 | }, 203 | { 204 | "name": "Maintained", 205 | "score": 10, 206 | "reason": "30 commit(s) out of 30 and 24 issue activity out of 30 found in the last 90 days -- score normalized to 10", 207 | "details": null, 208 | "documentation": { 209 | "short": "Determines if the project is \"actively maintained\".", 210 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#maintained" 211 | } 212 | }, 213 | { 214 | "name": "Packaging", 215 | "score": -1, 216 | "reason": "no published package detected", 217 | "details": ["Warn: no GitHub publishing workflow detected"], 218 | "documentation": { 219 | "short": "Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.", 220 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#packaging" 221 | } 222 | }, 223 | { 224 | "name": "Pinned-Dependencies", 225 | "score": 7, 226 | "reason": "dependency not pinned by hash detected -- score normalized to 7", 227 | "details": [ 228 | "Warn: containerImage not pinned by hash: .devcontainer/Dockerfile:1: pin your Docker image by updating nodejs/devcontainer:nightly to nodejs/devcontainer:nightly@sha256:8ee0a4f24a11055b41e57a5b2796a84e29d22e7d44eaf187a9fb7996dbe3e616", 229 | "Warn: containerImage not pinned by hash: deps/openssl/config/Dockerfile:1: pin your Docker image by updating ubuntu:20.04 to ubuntu:20.04@sha256:f8f658407c35733471596f25fdb4ed748b80e545ab57e84efbdb1dbbb01bd70e", 230 | "Warn: npmCommand not pinned by hash: .github/workflows/auto-start-ci.yml:60", 231 | "Warn: npmCommand not pinned by hash: .github/workflows/commit-queue.yml:79", 232 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux-without-intl.yml:51", 233 | "Warn: pipCommand not pinned by hash: .github/workflows/coverage-linux.yml:51", 234 | "Warn: npmCommand not pinned by hash: .github/workflows/daily-wpt-fyi.yml:113", 235 | "Warn: npmCommand not pinned by hash: .github/workflows/update-v8.yml:38", 236 | "Info: GitHub-owned GitHubActions are pinned", 237 | "Info: Third-party GitHubActions are pinned", 238 | "Info: no insecure (not pinned by hash) dependency downloads found in Dockerfiles", 239 | "Info: no insecure (not pinned by hash) dependency downloads found in shell scripts" 240 | ], 241 | "documentation": { 242 | "short": "Determines if the project has declared and pinned the dependencies of its build process.", 243 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#pinned-dependencies" 244 | } 245 | }, 246 | { 247 | "name": "SAST", 248 | "score": 0, 249 | "reason": "SAST tool is not run on all commits -- score normalized to 0", 250 | "details": [ 251 | "Warn: 0 commits out of 20 are checked with a SAST tool", 252 | "Warn: CodeQL tool not detected" 253 | ], 254 | "documentation": { 255 | "short": "Determines if the project uses static code analysis.", 256 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#sast" 257 | } 258 | }, 259 | { 260 | "name": "Security-Policy", 261 | "score": 10, 262 | "reason": "security policy file detected", 263 | "details": [ 264 | "Info: Found linked content in security policy: SECURITY.md", 265 | "Info: Found text in security policy: SECURITY.md", 266 | "Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md", 267 | "Info: security policy detected in current repo: SECURITY.md" 268 | ], 269 | "documentation": { 270 | "short": "Determines if the project has published a security policy.", 271 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#security-policy" 272 | } 273 | }, 274 | { 275 | "name": "Signed-Releases", 276 | "score": -1, 277 | "reason": "no releases found", 278 | "details": ["Warn: no GitHub releases found"], 279 | "documentation": { 280 | "short": "Determines if the project cryptographically signs release artifacts.", 281 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#signed-releases" 282 | } 283 | }, 284 | { 285 | "name": "Token-Permissions", 286 | "score": 10, 287 | "reason": "tokens are read-only in GitHub workflows", 288 | "details": [ 289 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:18", 290 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/auto-start-ci.yml:23", 291 | "Info: Medium severity: jobLevel 'contents' permission set to 'read': .github/workflows/auto-start-ci.yml:43", 292 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-tarball.yml:35", 293 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/build-windows.yml:30", 294 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stale-feature-requests.yml:32", 295 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stale-pull-requests.yml:29", 296 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/close-stalled.yml:13", 297 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/comment-labeled.yml:19", 298 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-lint.yml:9", 299 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/commit-queue.yml:22", 300 | "Info: Medium severity: jobLevel 'pull-requests' permission set to 'read': .github/workflows/commit-queue.yml:27", 301 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux-without-intl.yml:33", 302 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-linux.yml:33", 303 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/coverage-windows.yml:35", 304 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily-wpt-fyi.yml:24", 305 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/daily.yml:12", 306 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/doc.yml:20", 307 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-collaborators.yml:14", 308 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/find-inactive-tsc.yml:14", 309 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-flaky-test-issue.yml:8", 310 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/label-pr.yml:8", 311 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/license-builder.yml:10", 312 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/license-builder.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 313 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/linters.yml:21", 314 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/notify-on-push.yml:8", 315 | "Info: High severity: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:19", 316 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-asan.yml:37", 317 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-internet.yml:28", 318 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-linux.yml:30", 319 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/test-macos.yml:36", 320 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/timezone-update.yml:10", 321 | "Warn: Medium severity: jobLevel 'contents' permission set to 'write': .github/workflows/timezone-update.yml:15: Verify which permissions are needed and consider whether you can reduce them. (High effort)", 322 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/tools.yml:47", 323 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-openssl.yml:10", 324 | "Info: High severity: topLevel 'contents' permission set to 'read': .github/workflows/update-v8.yml:12" 325 | ], 326 | "documentation": { 327 | "short": "Determines if the project's workflows follow the principle of least privilege.", 328 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#token-permissions" 329 | } 330 | }, 331 | { 332 | "name": "Vulnerabilities", 333 | "score": 10, 334 | "reason": "no vulnerabilities detected", 335 | "details": null, 336 | "documentation": { 337 | "short": "Determines if the project has open, known unfixed vulnerabilities.", 338 | "url": "https://github.com/ossf/scorecard/blob/27cfe92ed356fdb5a398c919ad480817ea907808/docs/checks.md#vulnerabilities" 339 | } 340 | } 341 | ] 342 | } 343 | -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } 38 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scorecard-visualizer", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@tanstack/react-query": "4.29.1", 7 | "@tanstack/react-query-devtools": "4.29.1", 8 | "@testing-library/jest-dom": "6.6.3", 9 | "@testing-library/react": "16.2.0", 10 | "@testing-library/user-event": "14.6.1", 11 | "@types/jest": "29.5.14", 12 | "@types/node": "22.13.1", 13 | "@types/react": "18.3.3", 14 | "@types/react-dom": "18.3.0", 15 | "react": "18.3.1", 16 | "diff": "7.0.0", 17 | "react-dom": "18.3.1", 18 | "react-router-dom": "7.5.0", 19 | "react-scripts": "5.0.1", 20 | "typescript": "4.9.5", 21 | "web-vitals": "3.5.0" 22 | }, 23 | "homepage": "https://ossf.github.io/scorecard-visualizer", 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "lint": "eslint .", 27 | "lint:fix": "eslint . --fix", 28 | "format": "prettier . --loglevel silent", 29 | "format:fix": "prettier . --write", 30 | "build": "react-scripts build", 31 | "test": "react-scripts test", 32 | "test:e2e": "NODE_ENV=test cypress open", 33 | "test:e2e-ci": "NODE_ENV=test cypress run", 34 | "eject": "react-scripts eject" 35 | }, 36 | "eslintConfig": { 37 | "extends": [ 38 | "react-app", 39 | "react-app/jest", 40 | "prettier" 41 | ] 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "@types/diff": "7.0.1", 57 | "cypress": "14.0.3", 58 | "eslint-config-prettier": "10.0.1", 59 | "prettier": "3.5.3" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "jsxBracketSameLine": true 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 20 | OpenSSF Scorecard API visualizer 21 | 22 | 23 | 24 |
25 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossf/scorecard-visualizer/eb6bdae5c7042a271e5c579b7625dfcccdeafa99/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/components/Badge.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/Badge.css"; 2 | 3 | interface BadgeTypes { 4 | variant?: string; 5 | message: string; 6 | } 7 | 8 | function Badge(props: BadgeTypes) { 9 | return ( 10 | {props.message} 11 | ); 12 | } 13 | 14 | export default Badge; 15 | -------------------------------------------------------------------------------- /src/components/Collapsable.tsx: -------------------------------------------------------------------------------- 1 | import { ComparatorDiff } from "./ComparatorDiff"; 2 | import "../styles/Collapsable.css"; 3 | 4 | const Collapsible = (props: any) => { 5 | return ( 6 | <> 7 |
8 | Details 9 |
10 |
    11 | {props.details.map((el: string, i: number) => 12 | props.prevDetails && props.prevDetails[i] ? ( 13 |
  • 14 | 18 |
  • 19 | ) : ( 20 |
  • {el}
  • 21 | ), 22 | )} 23 |
24 |
25 |
26 | 27 | ); 28 | }; 29 | 30 | export default Collapsible; 31 | -------------------------------------------------------------------------------- /src/components/CommonError.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | COMMON_ERROR_MESSAGE, 3 | SPECIFIC_SERVER_ERROR_MESSAGE, 4 | TYPO_ERROR_MESSAGE, 5 | } from "../constants/commonErrorMessage"; 6 | 7 | type ErrorMessageProps = { 8 | errorMessage?: any; 9 | }; 10 | 11 | function CommonError({ errorMessage }: ErrorMessageProps) { 12 | if (errorMessage) { 13 | return ( 14 | <> 15 |

{errorMessage}

16 |

{SPECIFIC_SERVER_ERROR_MESSAGE}

17 | 18 | ); 19 | } 20 | return ( 21 |
22 |

{COMMON_ERROR_MESSAGE}

23 |

{TYPO_ERROR_MESSAGE}

24 |
25 | ); 26 | } 27 | 28 | export default CommonError; 29 | -------------------------------------------------------------------------------- /src/components/ComparatorDiff.tsx: -------------------------------------------------------------------------------- 1 | import * as Diff from "diff"; 2 | import "../styles/ComparatorDiff.css"; 3 | 4 | type Props = { 5 | previous: string; 6 | current: string; 7 | }; 8 | 9 | export const ComparatorDiff = (props: Props) => { 10 | const diff = Diff.diffLines(props.previous, props.current); 11 | return ( 12 | <> 13 | {diff.map((part: any, i: number) => ( 14 | 20 | {part.value} 21 | 22 | ))} 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/Loading.css"; 2 | function Loading() { 3 | return ( 4 | <> 5 |
6 |

Loading

7 | 8 | 9 | 10 |
11 | 12 | ); 13 | } 14 | 15 | export default Loading; 16 | -------------------------------------------------------------------------------- /src/components/NoAvailableDataMark.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/NoAvailableDataMark.css"; 2 | 3 | export default function NoAvailableDataMark() { 4 | return ( 5 | 6 | ? 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function NotFound() { 4 | return

Nothing to see here!

; 5 | } 6 | 7 | export default NotFound; 8 | -------------------------------------------------------------------------------- /src/components/ProjectComparator.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | import { getScorecardUrl } from "../utils/getScorecardUrl"; 5 | import { formatDate } from "../utils/formatDate"; 6 | import { scoreChecker } from "../utils/comparator/scoreChecker"; 7 | import CommonError from "./CommonError"; 8 | import Collapsible from "./Collapsable"; 9 | import Loading from "./Loading"; 10 | import NoAvailableDataMark from "./NoAvailableDataMark"; 11 | import { ComparatorDiff } from "./ComparatorDiff"; 12 | import { ScoreElement, ConsolidatedScoreElement } from "../types"; 13 | import { getRefinedChecks } from "../utils/comparator/getRefinedChecks"; 14 | import { areEqualElements } from "../utils/comparator/areEqualElements"; 15 | 16 | import "../styles/ProjectDetails.css"; 17 | 18 | function ProjectComparator() { 19 | const params = useParams(); 20 | const { platform, org, repo, prevCommitHash, currentCommitHash } = params; 21 | 22 | const [consolidatedData, setConsolidatedData] = useState< 23 | ConsolidatedScoreElement[] 24 | >([]); 25 | const [discrepancies, setDiscrepancies] = useState([]); 26 | 27 | const prevCommitQuery = useQuery({ 28 | queryKey: ["prevCommit"], 29 | queryFn: async () => { 30 | const response = await fetch( 31 | getScorecardUrl({ platform, org, repo, commitHash: prevCommitHash }), 32 | ); 33 | if (response.status >= 500) { 34 | throw new Error("An error ocurred. Invalid response from server"); 35 | } 36 | return response.json(); 37 | }, 38 | }); 39 | 40 | const currentCommitQuery = useQuery({ 41 | queryKey: ["currentCommit"], 42 | queryFn: async () => { 43 | const response = await fetch( 44 | getScorecardUrl({ platform, org, repo, commitHash: currentCommitHash }), 45 | ); 46 | if (response.status >= 500) { 47 | throw new Error("An error ocurred. Invalid response from server"); 48 | } 49 | return response.json(); 50 | }, 51 | }); 52 | 53 | const currentData = currentCommitQuery?.data; 54 | const previousData = prevCommitQuery?.data; 55 | 56 | useEffect(() => { 57 | const areEqualDetails = () => { 58 | if (!previousData?.checks || !previousData?.score) { 59 | return; 60 | } 61 | 62 | const refinedChecks = getRefinedChecks( 63 | previousData?.checks, 64 | currentData?.checks, 65 | ); 66 | 67 | const data = refinedChecks.common.map((name: string) => { 68 | const previousElement = previousData?.checks?.filter( 69 | (el: ScoreElement) => el.name === name, 70 | )[0]; 71 | const currentElement = currentData?.checks?.filter( 72 | (el: ScoreElement) => el.name === name, 73 | )[0]; 74 | return { 75 | areEqual: areEqualElements(currentElement, previousElement), 76 | name: previousElement.name, 77 | details: currentElement.details, 78 | reason: currentElement.reason, 79 | score: currentElement.score, 80 | short: currentElement.documentation.short, 81 | url: currentElement.documentation.url, 82 | prevDetails: previousElement.details, 83 | prevReason: previousElement.reason, 84 | prevScore: previousElement.score, 85 | }; 86 | }); 87 | setConsolidatedData(data); 88 | setDiscrepancies(refinedChecks.discrepancies); 89 | }; 90 | areEqualDetails(); 91 | }, [currentData, previousData]); 92 | 93 | if (prevCommitQuery.isLoading || currentCommitQuery.isLoading) { 94 | return ; 95 | } 96 | 97 | if (prevCommitQuery.error || currentCommitQuery.error) { 98 | return ; 99 | } 100 | 101 | return ( 102 | <> 103 |

OpenSSF Scorecard comparator for {`${org}/${repo}`}

104 |
105 |

{`Current Score: ${currentData.score}/10`}

106 | {scoreChecker(currentData.score, previousData.score)} 107 |
108 |

109 | Analysis of commits{" "} 110 | 115 | {`(${currentData.repo.commit.substring(0, 8)})`} 116 | {" "} 117 | and{" "} 118 | 123 | {`(${previousData.repo.commit.substring(0, 8)})`} 124 | 125 |

126 |

Date: {formatDate(currentData.date)}

127 |

128 | Scorecard version {currentData.scorecard.version}{" "} 129 | 134 | {`(${currentData.scorecard.commit.substring(0, 8)})`} 135 | 136 |

137 | {discrepancies.length > 0 && ( 138 | 139 | {`Scorecard API missing: ${discrepancies.join(", ")} checks. See `} 140 | 145 | details 146 | 147 | 148 | )} 149 |
150 | {Array.isArray(consolidatedData) && 151 | consolidatedData.map((element: ConsolidatedScoreElement) => ( 152 | <> 153 |
154 |
155 |
156 |

{element.name}

157 |
161 | {element.score >= 0 ? ( 162 | <> 163 | {scoreChecker(element.score, element.prevScore)} 164 | {element.score}/10 165 | 166 | ) : ( 167 | 168 | )} 169 |
170 |
171 |
172 | 173 |

174 | Description: {element.short.toLocaleLowerCase()}{" "} 175 | 176 | See documentation 177 | 178 |

179 | {!element.areEqual &&

Additional details / variations

} 180 | {!element.areEqual ? ( 181 |

182 | Reasoning:{" "} 183 | 187 |

188 | ) : ( 189 |

190 | Reasoning: {element?.reason.toLocaleLowerCase()} 191 |

192 | )} 193 | {Array.isArray(element.details) && ( 194 | 198 | )} 199 |
200 |
201 | 202 | ))} 203 | 204 | ); 205 | } 206 | 207 | export default ProjectComparator; 208 | -------------------------------------------------------------------------------- /src/components/ProjectDetails.tsx: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | import { useQuery } from "@tanstack/react-query"; 3 | 4 | import { getScorecardUrl } from "../utils/getScorecardUrl"; 5 | import { formatDate } from "../utils/formatDate"; 6 | import CommonError from "./CommonError"; 7 | import Collapsible from "./Collapsable"; 8 | import Loading from "./Loading"; 9 | import NoAvailableDataMark from "./NoAvailableDataMark"; 10 | import { ScoreElement } from "../types"; 11 | import { GITHUB } from "../constants/platforms"; 12 | 13 | import "../styles/ProjectDetails.css"; 14 | 15 | function ProjectDetails() { 16 | const params = useParams(); 17 | const { platform, org, repo, commitHash } = params; 18 | 19 | const { isLoading, error, data } = useQuery({ 20 | queryKey: ["projectData"], 21 | queryFn: async () => { 22 | const response = await fetch( 23 | getScorecardUrl({ platform, org, repo, commitHash }), 24 | ); 25 | if (response.status >= 500) { 26 | throw new Error("An error ocurred. Invalid response from server"); 27 | } 28 | return response.json(); 29 | }, 30 | }); 31 | 32 | if (isLoading) { 33 | return ; 34 | } 35 | 36 | if (error) { 37 | return ; 38 | } 39 | 40 | return ( 41 | <> 42 |

OpenSSF Scorecard for {`${org}/${repo}`}

43 |

{`Score: ${data.score}/10`}

44 |

Date: {formatDate(data.date)}

45 |

46 | Scorecard version {data.scorecard.version}{" "} 47 | 52 | {`(${data.scorecard.commit.substring(0, 8)})`} 53 | 54 |

55 |

56 | Current commit{" "} 57 | 62 | {`(${data.repo.commit.substring(0, 8)})`} 63 | 64 |

65 | {platform === GITHUB && ( 66 | <> 67 |

68 | Additional info at{" "} 69 | 74 | deps.dev 75 | 76 |

77 |

78 | Improve your scoring with{" "} 79 | 84 | StepSecurity 85 | 86 |

87 | 88 | )} 89 | 90 |
91 | {data.checks.map((element: ScoreElement) => ( 92 | <> 93 |
94 |
95 |

{element.name}

96 | {element.score !== -1 ? ( 97 | {element.score}/10 98 | ) : ( 99 | 100 | )} 101 |
102 |

103 | Description: {element.documentation.short.toLocaleLowerCase()}{" "} 104 | 109 | See documentation 110 | 111 |

112 |

Reasoning: {element?.reason.toLocaleLowerCase()}

113 | {Array.isArray(element.details) && ( 114 | 115 | )} 116 |
117 |
118 | 119 | ))} 120 | 121 | ); 122 | } 123 | 124 | export default ProjectDetails; 125 | -------------------------------------------------------------------------------- /src/constants/checks.ts: -------------------------------------------------------------------------------- 1 | export const CHECKS_LIST_NAMES = [ 2 | "Binary-Artifacts", 3 | "Branch-Protection", 4 | "CI-Tests", 5 | "CII-Best-Practices", 6 | "Code-Review", 7 | "Contributors", 8 | "Dangerous-Workflow", 9 | "Dependency-Update-Tool", 10 | "Fuzzing", 11 | "License", 12 | "Maintained", 13 | "Packaging", 14 | "Pinned-Dependencies", 15 | "SAST", 16 | "Security-Policy", 17 | "Signed-Releases", 18 | "Token-Permissions", 19 | "Vulnerabilities", 20 | ]; 21 | -------------------------------------------------------------------------------- /src/constants/commonErrorMessage.ts: -------------------------------------------------------------------------------- 1 | export const SERVER_ERROR_MESSAGE: string = 2 | "An error ocurred. Invalid response from server."; 3 | export const SPECIFIC_SERVER_ERROR_MESSAGE: string = 4 | "Please check if the commits are a valid reference or refresh."; 5 | export const COMMON_ERROR_MESSAGE: string = 6 | "An error ocurred. Please refresh/try again."; 7 | export const TYPO_ERROR_MESSAGE: string = 8 | "Please check if the org/repository/commit has been analysed by the Scorecard."; 9 | -------------------------------------------------------------------------------- /src/constants/platforms.ts: -------------------------------------------------------------------------------- 1 | export const GITHUB = "github.com"; 2 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 20px; 4 | font-family: "Montserrat", sans-serif; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | } 8 | 9 | #root { 10 | background-color: #fff; 11 | border-radius: 4px; 12 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.1); 13 | padding: 20px; 14 | margin-bottom: 20px; 15 | } 16 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { createHashRouter, RouterProvider } from "react-router-dom"; 4 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 5 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 6 | 7 | import NotFound from "./components/NotFound"; 8 | import CommonError from "./components/CommonError"; 9 | import ProjectDetails from "./components/ProjectDetails"; 10 | import ProjectComparator from "./components/ProjectComparator"; 11 | 12 | import reportWebVitals from "./reportWebVitals"; 13 | import "./index.css"; 14 | 15 | const router = createHashRouter([ 16 | { 17 | path: "*", 18 | element: , 19 | errorElement: , 20 | }, 21 | { 22 | path: "404", 23 | element: , 24 | }, 25 | { 26 | path: "projects/:platform/:org/:repo/compare/:prevCommitHash/:currentCommitHash", 27 | element: , 28 | errorElement: , 29 | }, 30 | { 31 | path: "projects/:platform/:org/:repo/commit/:commitHash", 32 | element: , 33 | errorElement: , 34 | }, 35 | { 36 | path: "projects/:platform/:org/:repo", 37 | element: , 38 | errorElement: , 39 | }, 40 | ]); 41 | 42 | const queryClient = new QueryClient(); 43 | 44 | const root = ReactDOM.createRoot( 45 | document.getElementById("root") as HTMLElement, 46 | ); 47 | root.render( 48 | 49 | 50 | 51 | 52 | 53 | , 54 | ); 55 | 56 | // If you want to start measuring performance in your app, pass a function 57 | // to log results (for example: reportWebVitals(console.log)) 58 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 59 | reportWebVitals(); 60 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from "web-vitals"; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | -------------------------------------------------------------------------------- /src/styles/Badge.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | width: auto; 3 | padding: 0.4rem; 4 | margin-right: 1rem; 5 | color: white; 6 | background-color: #6c757d; 7 | border-radius: 5px; 8 | font-size: 14px; 9 | display: inline-block; 10 | font-weight: 600; 11 | } 12 | 13 | .badge-increased { 14 | background-color: #188754; 15 | } 16 | 17 | .badge-decreased { 18 | background-color: #dc3545; 19 | } 20 | -------------------------------------------------------------------------------- /src/styles/Collapsable.css: -------------------------------------------------------------------------------- 1 | details { 2 | border: 1.3px solid black; 3 | border-radius: 5px; 4 | padding: 0.5em 0.5em 0; 5 | background-color: #f4f7f9; 6 | } 7 | 8 | summary { 9 | font-weight: bold; 10 | margin: -0.5em -0.5em 0; 11 | padding: 0.8em; 12 | cursor: pointer; 13 | } 14 | 15 | details[open] { 16 | padding: 0.5em; 17 | } 18 | 19 | details[open] summary { 20 | border-bottom: 1px solid black; 21 | margin-bottom: 0.5em; 22 | } 23 | -------------------------------------------------------------------------------- /src/styles/ComparatorDiff.css: -------------------------------------------------------------------------------- 1 | .text-addition { 2 | background-color: #acf2bd; 3 | } 4 | 5 | .text-removal { 6 | background-color: #fdb8c0; 7 | text-decoration: line-through; 8 | text-decoration-thickness: -2px; 9 | } 10 | -------------------------------------------------------------------------------- /src/styles/Loading.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | text-align: center; 3 | margin-top: 100px; 4 | } 5 | .loader span { 6 | display: inline-block; 7 | vertical-align: middle; 8 | width: 10px; 9 | height: 10px; 10 | margin: 50px auto; 11 | background: black; 12 | border-radius: 50px; 13 | animation: loader 0.9s infinite alternate; 14 | -moz-animation: loader 0.9s infinite alternate; 15 | } 16 | .loader span:nth-of-type(2) { 17 | animation-delay: 0.3s; 18 | -moz-animation-delay: 0.3s; 19 | } 20 | .loader span:nth-of-type(3) { 21 | animation-delay: 0.6s; 22 | -moz-animation-delay: 0.6s; 23 | } 24 | 25 | @keyframes loader { 26 | 0% { 27 | width: 10px; 28 | height: 10px; 29 | opacity: 0.9; 30 | -webkit-transform: translateY(0); 31 | } 32 | 100% { 33 | width: 24px; 34 | height: 24px; 35 | opacity: 0.1; 36 | -webkit-transform: translateY(-21px); 37 | } 38 | } 39 | @-moz-keyframes loader { 40 | 0% { 41 | width: 10px; 42 | height: 10px; 43 | opacity: 0.9; 44 | -moz-transform: translateY(0); 45 | } 46 | 100% { 47 | width: 24px; 48 | height: 24px; 49 | opacity: 0.1; 50 | -moz-transform: translateY(-21px); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/styles/NoAvailableDataMark.css: -------------------------------------------------------------------------------- 1 | .not-available-data { 2 | display: inline-block; 3 | color: red; 4 | font-size: 24px; 5 | font-weight: 900; 6 | } 7 | 8 | .tooltip { 9 | position: relative; 10 | cursor: pointer; 11 | font-size: 14px; 12 | } 13 | 14 | .tooltip::before { 15 | content: attr(data-tooltip); 16 | position: absolute; 17 | bottom: 100%; 18 | left: 50%; 19 | transform: translate(-50%); 20 | margin-bottom: 15px; 21 | color: #fff; 22 | background: rgba(0, 0, 0, 0.5); 23 | border-radius: 5px; 24 | padding: 5px; 25 | } 26 | 27 | .tooltip::after { 28 | position: absolute; 29 | content: ""; 30 | width: 0; 31 | height: 0; 32 | border-left: 5px solid transparent; 33 | border-right: 5px solid transparent; 34 | border-top: 7px solid rgba(0, 0, 0, 0.5); 35 | } 36 | 37 | .tooltip::before, 38 | .tooltip::after { 39 | opacity: 0; 40 | visibility: hidden; 41 | transition: opacity 0.3s ease-in-out; 42 | } 43 | 44 | .tooltip:hover::before, 45 | .tooltip:hover::after { 46 | opacity: 1; 47 | visibility: visible; 48 | } 49 | 50 | .tooltip--top::before, 51 | .tooltip--top::after { 52 | bottom: 100%; 53 | left: 50%; 54 | transform: translate(-50%); 55 | margin-bottom: 15px; 56 | } 57 | 58 | .tooltip--top::after { 59 | margin-bottom: 8px; 60 | border-left: 5px solid transparent; 61 | border-right: 5px solid transparent; 62 | border-top: 7px solid rgba(0, 0, 0, 0.5); 63 | } 64 | -------------------------------------------------------------------------------- /src/styles/ProjectDetails.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | padding: 40px; 9 | } 10 | 11 | /* Style the headings and other elements */ 12 | h1 { 13 | font-size: 28px; 14 | margin: 0.5em 0 0.8em 0; 15 | font-weight: 600; 16 | } 17 | 18 | h2 { 19 | font-size: 24px; 20 | margin-right: 1rem; 21 | } 22 | 23 | h3 { 24 | font-size: 22px; 25 | margin-right: 1rem; 26 | } 27 | 28 | h4 { 29 | color: orange; 30 | margin: 2rem 0 1rem 0; 31 | } 32 | 33 | p { 34 | margin: 0 0 1em; 35 | } 36 | 37 | hr { 38 | border: none; 39 | border-top: 1px solid #eee; 40 | margin: 1.5rem 0 0 0; 41 | } 42 | 43 | div > div > span { 44 | font-size: 24px; 45 | font-weight: bold; 46 | } 47 | 48 | div > div > div { 49 | margin-top: 10px; 50 | } 51 | 52 | ul { 53 | list-style-type: disc; 54 | } 55 | 56 | a { 57 | color: #007bff; 58 | } 59 | 60 | a:hover { 61 | text-decoration: underline; 62 | } 63 | 64 | /* Style the wrapper elements */ 65 | .card__wrapper { 66 | padding: 0.7rem; 67 | } 68 | 69 | .card__wrapper li { 70 | margin-bottom: 0.7rem; 71 | } 72 | 73 | .heading__wrapper { 74 | display: flex; 75 | align-items: center; 76 | justify-content: space-between; 77 | } 78 | 79 | .details__wrapper { 80 | background-color: #f4f7f9; 81 | padding-top: 0.5rem; 82 | padding-bottom: 0.5rem; 83 | font-size: 14px; 84 | font-weight: 500; 85 | font-family: monospace; 86 | letter-spacing: 0.3px; 87 | line-height: 1.2rem; 88 | overflow-x: scroll; 89 | } 90 | 91 | .score-wrapper { 92 | display: flex; 93 | flex-direction: row; 94 | align-items: center; 95 | } 96 | 97 | .info-badge__wrapper { 98 | width: 100%; 99 | display: flex; 100 | flex-direction: row; 101 | align-items: center; 102 | justify-content: space-between; 103 | } 104 | 105 | .info-score__wrapper { 106 | display: flex; 107 | align-items: center; 108 | } 109 | 110 | .warning-message { 111 | width: auto; 112 | padding: 10px; 113 | color: black; 114 | background-color: orange; 115 | border-radius: 5px; 116 | font-size: 1rem; 117 | display: inline-block; 118 | font-weight: 600; 119 | line-height: 1.5rem; 120 | } 121 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface ScoreElement { 2 | name: string; 3 | score: number; 4 | reason: string; 5 | details: string[]; 6 | documentation: { 7 | short: string; 8 | url: string; 9 | }; 10 | } 11 | 12 | export interface ConsolidatedScoreElement { 13 | areEqual: boolean; 14 | name: string; 15 | details: string[]; 16 | reason: string; 17 | score: number; 18 | short: string; 19 | url: string; 20 | prevDetails: string[]; 21 | prevReason: string; 22 | prevScore: number; 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/comparator/areEqualElements.test.js: -------------------------------------------------------------------------------- 1 | import { areEqualElements } from "./areEqualElements.tsx"; 2 | 3 | const baseScoreElement = { 4 | name: "foo", 5 | score: 0, 6 | reason: "sample reason", 7 | details: ["sample detail"], 8 | documentation: { 9 | short: "documentation short", 10 | url: "documentation url", 11 | }, 12 | }; 13 | 14 | describe("util: areEqualElements", () => { 15 | it("returns true if two elements are equal", () => { 16 | expect( 17 | areEqualElements( 18 | { ...baseScoreElement, details: ["foo"], reason: "foo" }, 19 | { ...baseScoreElement, details: ["foo"], reason: "foo" }, 20 | ), 21 | ).toBe(true); 22 | }); 23 | 24 | it("returns false if two elements are not equal", () => { 25 | expect( 26 | areEqualElements( 27 | { ...baseScoreElement, details: ["foo"], reason: "foo" }, 28 | { ...baseScoreElement, details: ["bar"], reason: "bar" }, 29 | ), 30 | ).toBe(false); 31 | expect( 32 | areEqualElements( 33 | { ...baseScoreElement, details: ["foo"], reason: "foo" }, 34 | { ...baseScoreElement, details: ["bar"], reason: "foo" }, 35 | ), 36 | ).toBe(false); 37 | expect( 38 | areEqualElements( 39 | { ...baseScoreElement, details: ["foo"], reason: "foo" }, 40 | { ...baseScoreElement, details: ["foo"], reason: "bar" }, 41 | ), 42 | ).toBe(false); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/utils/comparator/areEqualElements.tsx: -------------------------------------------------------------------------------- 1 | import { ScoreElement } from "../../types"; 2 | 3 | export const areEqualElements = ( 4 | currentElement: ScoreElement, 5 | previousElement: ScoreElement, 6 | ) => 7 | JSON.stringify(currentElement.details) === 8 | JSON.stringify(previousElement.details) && 9 | JSON.stringify(currentElement.reason) === 10 | JSON.stringify(previousElement.reason); 11 | -------------------------------------------------------------------------------- /src/utils/comparator/getRefinedChecks.test.js: -------------------------------------------------------------------------------- 1 | import { getRefinedChecks } from "./getRefinedChecks.tsx"; 2 | import { CHECKS_LIST_NAMES } from "../../constants/checks.ts"; 3 | import sample1 from "../../../cypress/fixtures/2ac5e9889aba461f5a54d320973d2574980d206b.json"; 4 | import sample2 from "../../../cypress/fixtures/077fd7d83d7d41695137c1af5b9be1d72250e69e.json"; 5 | 6 | describe("util: getRefinedChecks", () => { 7 | it("Should handle empty checks", () => { 8 | expect(getRefinedChecks([], [])).toEqual({ 9 | common: [], 10 | discrepancies: [], 11 | }); 12 | }); 13 | 14 | it("Should handle an empty check", () => { 15 | expect(getRefinedChecks(sample1.checks, [])).toEqual({ 16 | common: [], 17 | discrepancies: CHECKS_LIST_NAMES, 18 | }); 19 | 20 | expect(getRefinedChecks([], sample1.checks)).toEqual({ 21 | common: [], 22 | discrepancies: CHECKS_LIST_NAMES, 23 | }); 24 | }); 25 | 26 | it("Should generate a refined valid result", () => { 27 | expect(getRefinedChecks(sample1.checks, sample2.checks)).toEqual({ 28 | common: CHECKS_LIST_NAMES, 29 | discrepancies: [], 30 | }); 31 | }); 32 | 33 | it("Should handle discrepancies and provide a valid result", () => { 34 | const filteredList = sample1.checks.filter( 35 | (item) => item.name !== "Binary-Artifacts", 36 | ); 37 | expect(getRefinedChecks(filteredList, sample2.checks)).toEqual({ 38 | common: filteredList.map((item) => item.name), 39 | discrepancies: ["Binary-Artifacts"], 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/utils/comparator/getRefinedChecks.tsx: -------------------------------------------------------------------------------- 1 | import { ScoreElement } from "../../types"; 2 | import { CHECKS_LIST_NAMES } from "../../constants/checks"; 3 | 4 | export const getRefinedChecks = ( 5 | from: ScoreElement[] = [], 6 | to: ScoreElement[] = [], 7 | ) => { 8 | const haveMaxChecksNumber = 9 | from.length === CHECKS_LIST_NAMES.length && 10 | to.length === CHECKS_LIST_NAMES.length; 11 | 12 | if (haveMaxChecksNumber) { 13 | return { 14 | common: CHECKS_LIST_NAMES, 15 | discrepancies: [], 16 | }; 17 | } 18 | 19 | const fromNames = from.map((el: ScoreElement) => el.name); 20 | const toNames = to.map((el: ScoreElement) => el.name); 21 | const allNames = new Set([...fromNames, ...toNames]); 22 | const common = []; 23 | const discrepancies = []; 24 | 25 | for (const name of allNames) { 26 | if (fromNames.includes(name) && toNames.includes(name)) { 27 | common.push(name); 28 | } else { 29 | discrepancies.push(name); 30 | } 31 | } 32 | 33 | return { 34 | common: common.sort(), 35 | discrepancies: discrepancies.sort(), 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /src/utils/comparator/scoreChecker.tsx: -------------------------------------------------------------------------------- 1 | import Badge from "../../components/Badge"; 2 | 3 | export const scoreChecker = (currentValue: number, previousValue: number) => { 4 | if (currentValue < 0) { 5 | currentValue = 0; 6 | } 7 | 8 | if (previousValue < 0) { 9 | previousValue = 0; 10 | } 11 | 12 | const result = currentValue - previousValue; 13 | 14 | if (result > 0) { 15 | return ( 16 | 17 | ); 18 | } 19 | 20 | if (result < 0) { 21 | return ( 22 | 23 | ); 24 | } 25 | 26 | return ; 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/formatDate.test.ts: -------------------------------------------------------------------------------- 1 | import { formatDate } from "./formatDate"; 2 | 3 | describe("Format date", () => { 4 | it("should return the date formatted as expected", () => { 5 | const dateToFormat = "2023-07-02T10:27:18.412Z"; 6 | expect(formatDate(dateToFormat)).toBe("July 2, 2023"); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/utils/formatDate.ts: -------------------------------------------------------------------------------- 1 | interface Options { 2 | year?: "numeric" | "2-digit"; 3 | month?: "numeric" | "2-digit" | "long" | "short" | "narrow"; 4 | day?: "numeric" | "2-digit"; 5 | } 6 | 7 | export const formatDate = (date: string) => { 8 | const options: Options = { year: "numeric", month: "long", day: "numeric" }; 9 | return new Date(date).toLocaleDateString("en-US", options); 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/getScorecardUrl.test.ts: -------------------------------------------------------------------------------- 1 | import { getScorecardUrl } from "./getScorecardUrl"; 2 | 3 | describe("Get Scorecard Url", () => { 4 | const params = { 5 | platform: "github.com", 6 | org: "nodejs", 7 | repo: "node", 8 | commitHash: undefined, 9 | }; 10 | it("Should return the scorecard url", () => { 11 | expect(getScorecardUrl(params)).toBe( 12 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node", 13 | ); 14 | }); 15 | it("Should return the scorecard url with commit hash", () => { 16 | expect( 17 | getScorecardUrl({ 18 | ...params, 19 | commitHash: "1cea384480a6dea80128e5e0ddb714df7bea1520", 20 | }), 21 | ).toBe( 22 | "https://api.securityscorecards.dev/projects/github.com/nodejs/node/?commit=1cea384480a6dea80128e5e0ddb714df7bea1520", 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/utils/getScorecardUrl.ts: -------------------------------------------------------------------------------- 1 | interface paramsTypes { 2 | platform: string | undefined; 3 | org: string | undefined; 4 | repo: string | undefined; 5 | commitHash: string | undefined; 6 | } 7 | 8 | export const getScorecardUrl = (params: paramsTypes): string => { 9 | let baseUrl = `https://api.securityscorecards.dev/projects/${params.platform}/${params.org}/${params.repo}`; 10 | if (params.commitHash) { 11 | baseUrl += `/?commit=${params.commitHash}`; 12 | } 13 | return baseUrl; 14 | }; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | --------------------------------------------------------------------------------