diff --git a/__fixtures__/index.js b/__fixtures__/index.js index 812f53f..f91ae6a 100644 --- a/__fixtures__/index.js +++ b/__fixtures__/index.js @@ -10,7 +10,7 @@ const sampleGithubOrg = { issues_url: 'https://api.github.com/orgs/github/issues', members_url: 'https://api.github.com/orgs/github/members{/member}', public_members_url: 'https://api.github.com/orgs/github/public_members{/member}', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', description: 'A great organization', name: 'github', company: 'GitHub', @@ -76,7 +76,7 @@ const sampleGithubListOrgRepos = [ login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', @@ -197,7 +197,7 @@ const sampleGithubRepository = { login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', @@ -303,7 +303,7 @@ const sampleGithubRepository = { login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', @@ -435,7 +435,7 @@ const sampleGithubRepository = { login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', @@ -460,7 +460,7 @@ const sampleGithubRepository = { login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', @@ -582,7 +582,7 @@ const sampleGithubRepository = { login: 'octocat', id: 1, node_id: 'MDQ6VXNlcjE=', - avatar_url: 'https://github.com/images/error/octocat_happy.gif', + avatar_url: 'https://avatars.githubusercontent.com/u/9919?v=4', gravatar_id: '', url: 'https://api.github.com/users/octocat', html_url: 'https://github.com/octocat', diff --git a/__tests__/__snapshots__/providers.test.js.snap b/__tests__/__snapshots__/providers.test.js.snap index 8921bf5..b994e17 100644 --- a/__tests__/__snapshots__/providers.test.js.snap +++ b/__tests__/__snapshots__/providers.test.js.snap @@ -3,7 +3,7 @@ exports[`GitHub Provider mappers Should map organization data correctly 1`] = ` { "advanced_security_enabled_for_new_repositories": false, - "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", "blog": "https://github.com/blog", "collaborators": 8, "company": "GitHub", diff --git a/__tests__/cli/__snapshots__/workflows.test.js.snap b/__tests__/cli/__snapshots__/workflows.test.js.snap index 0496d20..b2d158c 100644 --- a/__tests__/cli/__snapshots__/workflows.test.js.snap +++ b/__tests__/cli/__snapshots__/workflows.test.js.snap @@ -27,5 +27,10 @@ exports[`list - Non-Interactive Mode Should provide a list of available workflow "name": "show-reports", "workflow": [Function], }, + { + "description": "Generate the reports for the stored data.", + "name": "generate-reports", + "workflow": [Function], + }, ] `; diff --git a/package-lock.json b/package-lock.json index baa14f8..2dcabd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "commander": "12.1.0", "date-fns": "4.1.0", "debug": "4.3.7", + "ejs": "3.1.10", "finalhandler": "1.3.1", "inquirer": "12.1.0", "knex": "3.1.0", @@ -2388,6 +2389,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -2533,7 +2540,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/batch": { @@ -2558,7 +2564,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -2728,7 +2733,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -2868,7 +2872,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { @@ -3146,6 +3149,21 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.67", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", @@ -4102,6 +4120,36 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4477,7 +4525,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5276,6 +5323,24 @@ "node": ">= 0.4" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -6423,7 +6488,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -8247,7 +8311,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" diff --git a/package.json b/package.json index b2d6cd4..180dc93 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "db:seed": "knex seed:run" }, "keywords": [ - "security", + "security", "CLI" ], "repository": { @@ -45,6 +45,7 @@ "commander": "12.1.0", "date-fns": "4.1.0", "debug": "4.3.7", + "ejs": "3.1.10", "finalhandler": "1.3.1", "inquirer": "12.1.0", "knex": "3.1.0", diff --git a/src/cli/workflows.js b/src/cli/workflows.js index 2c079d6..c244d22 100644 --- a/src/cli/workflows.js +++ b/src/cli/workflows.js @@ -1,6 +1,7 @@ const inquirer = require('inquirer').default const debug = require('debug')('cli:workflows') const { updateGithubOrgs, upsertGithubRepositories, runAllTheComplianceChecks, upsertOSSFScorecardAnalysis } = require('../workflows') +const { generateReports } = require('../reports') const { logger } = require('../utils') const commandList = [{ @@ -23,6 +24,10 @@ const commandList = [{ name: 'show-reports', description: 'Starts a http server that shows all the files and folders in the output directory.', workflow: require('../httpServer') +}, { + name: 'generate-reports', + description: 'Generate the reports for the stored data.', + workflow: generateReports }] const validCommandNames = commandList.map(({ name }) => name) diff --git a/src/reports/assets/favicon.ico b/src/reports/assets/favicon.ico new file mode 100644 index 0000000..f5af247 Binary files /dev/null and b/src/reports/assets/favicon.ico differ diff --git a/src/reports/index.js b/src/reports/index.js new file mode 100644 index 0000000..4eb4e97 --- /dev/null +++ b/src/reports/index.js @@ -0,0 +1,108 @@ +const { logger } = require('../utils') +const ejs = require('ejs') +const { mkdir, readdir, copyFile, readFile, writeFile } = require('node:fs').promises +const { join } = require('path') +const { initializeStore } = require('../store') + +const indexTemplatePath = join(process.cwd(), 'src', 'reports', 'templates', 'index.html.ejs') +const projectTemplatePath = join(process.cwd(), 'src', 'reports', 'templates', 'project.html.ejs') +const assetsFolder = join(process.cwd(), 'src', 'reports', 'assets') +const destinationFolder = join(process.cwd(), 'output') +const copyFolder = async (from, to) => { + try { + // Ensure the target directory exists + await mkdir(to, { recursive: true }) + + // Read the contents of the source directory + const entries = await readdir(from, { withFileTypes: true }) + + // Process each entry + await Promise.all( + entries.map(async (entry) => { + const source = join(from, entry.name) + const dest = join(to, entry.name) + + if (entry.isDirectory()) { + // Recursively copy subdirectories + await copyFolder(source, dest) + } else { + // Copy files + await copyFile(source, dest) + } + }) + ) + } catch (error) { + logger.warn(`Error copying folder from "${from}" to "${to}":`) + throw error + } +} + +const generateReports = async (knex) => { + logger.info('Generating reports') + const { getAllProjects, getAllChecklists, getAllComplianceChecks, getAllAlerts, getAllResults, getAllTasks, getAllGithubOrganizationsByProjectsId, getAllGithubRepositories, getAllOSSFResults } = initializeStore(knex) + // @TODO: Run the queries in parallel + const projects = await getAllProjects() + const checklists = await getAllChecklists() + const checks = await getAllComplianceChecks() + const alerts = await getAllAlerts() + const results = await getAllResults() + const tasks = await getAllTasks() + const ossfScorecardResults = await getAllOSSFResults() + const githubRepos = await getAllGithubRepositories() + + // @TODO: Read the files in parallel + const indexTemplate = await readFile(indexTemplatePath, 'utf8') + const projectTemplate = await readFile(projectTemplatePath, 'utf8') + await mkdir(join(destinationFolder, 'projects'), { recursive: true }) + + // Collecting data from the database + const indexData = { + projects, + checklists, + checks + } + + const projectsData = {} + + for (const project of projects) { + const githubOrgs = await getAllGithubOrganizationsByProjectsId([project.id]) + const githubOrgsIds = githubOrgs.map(org => org.id) + const githubReposInScope = githubRepos.filter(repo => githubOrgsIds.includes(repo.github_organization_id)) + const githubReposInScopeIds = githubReposInScope.map(repo => repo.id) + + projectsData[project.name] = { + project, + checks, + alerts: alerts.filter(alert => alert.project_id === project.id), + results: results.filter(result => result.project_id === project.id), + tasks: tasks.filter(task => task.project_id === project.id), + githubOrgs, + githubRepos: githubReposInScope, + ossfScorecardResults: ossfScorecardResults.filter(ossfResult => githubReposInScopeIds.includes(ossfResult.github_repository_id)) + } + + // Populate the project HTML template + const projectHtml = ejs.render(projectTemplate, projectsData[project.name]) + const projectFilename = join(destinationFolder, 'projects', `${project.name}.html`) + await writeFile(projectFilename, projectHtml) + } + + // @TODO: Validate against JSON Schemas + + // @TODO: Save the files in parallel + await writeFile('output/index_data.json', JSON.stringify(indexData, null, 2)) + await writeFile('output/projects/projects_data.json', JSON.stringify(projectsData, null, 2)) + + // copy assets folder + await copyFolder(assetsFolder, join(destinationFolder, 'assets')) + + // Populate the index HTML template + const indexHtml = ejs.render(indexTemplate, indexData) + + // Save the index HTML file + await writeFile('output/index.html', indexHtml) +} + +module.exports = { + generateReports +} diff --git a/src/reports/templates/index.html.ejs b/src/reports/templates/index.html.ejs new file mode 100644 index 0000000..ce1d3aa --- /dev/null +++ b/src/reports/templates/index.html.ejs @@ -0,0 +1,109 @@ + + + + + + + VisionBoard Reports + + + + + + +
+
+

VisionBoard Reports

+
+
+ +
+
+

Welcome!

+

In this dashboard, you can find the status of all the projects that you + registered. Every project listed includes additional reports with its own dashboard, tasks, and alerts. +

+ +

Projects

+

Click on any project to get more details:

+ + +

Checklist

+ + + + + + + + + + + <% checklists.forEach(item=> { %> + + + + + + + + <% }) %> + +
DocumentationTitleDescriptionAuthor
+ + <%= item.code_name %> + + + <%= item.title %> + + <%= item.description %> + + <%= item.author %> +
+ +

Compliance Checks

+

Currently + <%= checks.filter(i=> i.implementation_status === "completed").length %>/<%= checks.length %> + are implemented. Help us to implement more!

+ + + + + + + + + + + <% checks.forEach(item=> { %> + + + + + + <% }) %> + +
DocumentationNameDescription
+ + <%= item.code_name %> + + + <%= item.title %> + + <%= item.description %> +
+
+
+ + + \ No newline at end of file diff --git a/src/reports/templates/project.html.ejs b/src/reports/templates/project.html.ejs new file mode 100644 index 0000000..8605c29 --- /dev/null +++ b/src/reports/templates/project.html.ejs @@ -0,0 +1,250 @@ + + + + + + + VisionBoard Reports + + + + + + +
+
+

VisionBoard Reports

+
+
+
+
+

+ <%= project.name %> Report +

+

In this dashboard, you can find the relevant information for the project + <%= project.name %> + including results, tasks, and alerts. +

+ +

In order to elaborate this report we have analyzed + <%= githubOrgs.length %> GitHub org(s) + and + <%= githubRepos.length %> repositories + .

+ + +

Alerts

+
+ <% if (alerts && alerts.length > 0) { %> +
    + <% alerts.forEach(alert => { %> +
  • +

    + <%= alert.title %> +

    +

    + <%= alert.description %> +

    + View details +
  • + <% }) %> +
+ <% } else { %> +

No alerts available.

+ <% } %> +
+ + +

Results

+ <% if (tasks && tasks.length > 0) { %> + + + + + + + + + + + + <% results.forEach(item => { %> + + + + + + + <% }) %> + +
Check NameRationaleSeverityStatus
+ + <%= checks.filter(i => i.id === item.compliance_check_id)[0].code_name %> + + + <%= item.rationale %> + + <%= item.severity %> + + <%= item.status %> +
+ <% } else { %> +

No results available.

+ <% } %> + + +

Tasks

+
+ <% if (tasks && tasks.length > 0) { %> + <% tasks.forEach(task => { %> +
+ + + + + +
+

+ <%= task.title %> ( + View details + ) +

+
+
+ <% }) %> + <% } else { %> +
+

🎉 All tasks completed! Great job! 🎉

+
+ <% } %> +
+ + + +

OSSF Scorecard Analysis

+ <% if (ossfScorecardResults && ossfScorecardResults.length > 0) { %> + + + + + + + + + + + + + <% ossfScorecardResults.forEach(item => { %> + <% currentRepo = githubRepos.filter(i => i.id === item.github_repository_id)[0] %> + + + + + + + + <% }) %> + +
RepositoryCommitDurationDateScore
+ + <%= currentRepo.full_name %> + + + + <%= item.repo_commit %> + + + <%= item.analysis_execution_time / 1000 %>s + + <%= new Date(item.analysis_time).toISOString().slice(0, -5) %> + + <%= item.analysis_score %> +
+ <% } else { %> +

No OSSF Scorecard Results available.

+ <% } %> + + + +

GitHub Organizations in scope

+ <% if (githubOrgs && githubOrgs.length > 0) { %> + + <% } else { %> +

No results available.

+ <% } %> + + + +

GitHub repositories in scope

+ + <% if (githubRepos && githubRepos.length > 0) { %> + + + + + + + + + + + + + <% githubRepos.forEach(item => { %> + + + + + + + + <% }) %> + +
RepositoryStarsForksSubscribersOpen Issues
+ + <%= item.full_name %> + + + <%= item.stargazers_count %> + + <%= item.forks_count %> + + <%= item.subscribers_count %> + + <%= item.open_issues_count %> +
+ <% } else { %> +

No OSSF Scorecard Results available.

+ <% } %> +
+
+ + \ No newline at end of file diff --git a/src/schemas/githubListOrgRepos.json b/src/schemas/githubListOrgRepos.json index 05ff91c..a3a8246 100644 --- a/src/schemas/githubListOrgRepos.json +++ b/src/schemas/githubListOrgRepos.json @@ -70,7 +70,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { diff --git a/src/schemas/githubOrganization.json b/src/schemas/githubOrganization.json index 26c06c2..911f655 100644 --- a/src/schemas/githubOrganization.json +++ b/src/schemas/githubOrganization.json @@ -69,7 +69,7 @@ "avatar_url": { "type": "string", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "description": { diff --git a/src/schemas/githubRepository.json b/src/schemas/githubRepository.json index 5c86a25..c482cf3 100644 --- a/src/schemas/githubRepository.json +++ b/src/schemas/githubRepository.json @@ -68,7 +68,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { @@ -804,7 +804,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { @@ -1713,7 +1713,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { @@ -1973,7 +1973,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { @@ -2818,7 +2818,7 @@ "type": "string", "format": "uri", "examples": [ - "https://github.com/images/error/octocat_happy.gif" + "https://avatars.githubusercontent.com/u/9919?v=4" ] }, "gravatar_id": { diff --git a/src/store/index.js b/src/store/index.js index 19b745f..ad1d906 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -101,6 +101,9 @@ const getAllChecksInChecklistById = (knex, checklistId) => const getAllGithubOrganizationsByProjectsId = (knex, projectIds) => { debug(`Fetching all github organizations by projects id (${projectIds})...`) + if (!Array.isArray(projectIds)) { + throw new Error('projectIds must be an array') + } return knex('github_organizations') .whereIn('github_organizations.project_id', projectIds) .select('*') @@ -144,7 +147,8 @@ const initializeStore = (knex) => { addGithubRepo: (repo) => addTo('github_repositories', repo), addOSSFScorecardResult: (ossf) => addTo('ossf_scorecard_results', ossf), upsertOSSFScorecard: upsertOSSFScorecard(knex), - upsertComplianceCheckResult: upsertComplianceCheckResult(knex) + upsertComplianceCheckResult: upsertComplianceCheckResult(knex), + getAllOSSFResults: () => getAll('ossf_scorecard_results') } }