Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions __fixtures__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion __tests__/__snapshots__/providers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions __tests__/cli/__snapshots__/workflows.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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],
},
]
`;
77 changes: 70 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"db:seed": "knex seed:run"
},
"keywords": [
"security",
"security",
"CLI"
],
"repository": {
Expand All @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions src/cli/workflows.js
Original file line number Diff line number Diff line change
@@ -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 = [{
Expand All @@ -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)
Expand Down
Binary file added src/reports/assets/favicon.ico
Binary file not shown.
108 changes: 108 additions & 0 deletions src/reports/index.js
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading