diff --git a/src/main.ts b/src/main.ts index f6a1ea3..2965044 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ import { filterAllowedAdvisories } from '../src/filter' import {getInvalidLicenseChanges} from './licenses' +import {getScorecardLevels} from './scorecard' import * as summary from './summary' import {getRefs} from './git-refs' @@ -109,6 +110,8 @@ async function run(): Promise { } ) + core.debug(await getScorecardLevels(filteredChanges)) + core.debug(`Filtered Changes: ${JSON.stringify(filteredChanges)}`) core.debug(`Config Deny Packages: ${JSON.stringify(config)}`) diff --git a/src/schemas.ts b/src/schemas.ts index 2324fa1..7265d68 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -100,9 +100,52 @@ export const ComparisonResponseSchema = z.object({ snapshot_warnings: z.string() }) +export const DepsDevProjectSchema = z.object({ + projectKey: z.object({ + id: z.string({}), + openIssuesCount: z.string(), + starsCount: z.string(), + forksCount: z.string(), + license: z.string(), + description: z.string(), + homepage: z.string(), + scorecard: z.object({ + date: z.string(), + repository: z.object({ + name: z.string(), + commit: z.string() + }), + scorecard: z.object({ + version: z.string(), + commit: z.string() + }), + checks: z.array( + z.object({ + name: z.string(), + documentation: z.object({ + shortDescription: z.string(), + url: z.string() + }), + score: z.string(), + reason: z.string(), + details: z.array(z.string()) + }) + ), + overallScore: z.number() + }), + ossFuzz: z.object({ + lineCount: z.string(), + lineCoverCount: z.string(), + date: z.string(), + configUrl: z.string() + }) + }) +}) + export type Change = z.infer export type Changes = z.infer export type ComparisonResponse = z.infer export type ConfigurationOptions = z.infer export type Severity = z.infer export type Scope = (typeof SCOPES)[number] +export type DepsDevProject = z.infer diff --git a/src/scorecard.ts b/src/scorecard.ts new file mode 100644 index 0000000..d49bf5c --- /dev/null +++ b/src/scorecard.ts @@ -0,0 +1,51 @@ +import {Change, Changes, DepsDevProject, DepsDevProjectSchema} from './schemas' +import {isSPDXValid, octokitClient} from './utils' +import {PackageURL} from 'packageurl-js' + +/** + * Loops through a list of changes, filtering and returning the + * ones that don't conform to the licenses allow/deny lists. + * It will also filter out the changes which are defined in the licenseExclusions list. + * + * Keep in mind that we don't let users specify both an allow and a deny + * list in their config files, so this code works under the assumption that + * one of the two list parameters will be empty. If both lists are provided, + * we will ignore the deny list. + * @param {Change[]} changes The list of changes to filter. + * @param { { allow?: string[], deny?: string[], licenseExclusions?: string[]}} licenses An object with `allow`/`deny`/`licenseExclusions` keys, each containing a list of licenses. + * @returns {Promise<{Object.>}} A promise to a Record Object. The keys are strings, unlicensed, unresolved and forbidden. The values are a list of changes + */ + +export async function getScorecardLevels(changes: Change[]): Promise { + changes.forEach((change) => { + const purl = PackageURL.fromString(change.package_url) + const ecosystem = purl.type + const package = purl.name + const version = purl.version + return getDepsDevData(ecosystem, package, String(version)); + } +} + +const depsDevAPIRoot = 'https://api.deps.dev' + +async function getDepsDevData(ecosystem: String, package: String, version: String): Promise { + //Query deps.dev GetVersion API + const url = `${depsDevAPIRoot}//v3alpha/systems/${ecosystem}/packages/${package}/versions/${version}`; + const response = await fetch(url); + const data = await response.json(); + + //Get the related projects + const projects = data.relatedProjects; + projects.forEach((project) => { + return getDepsDevProjectData(project.projectKey); + } +} + +async function getDepsDevProjectData(projectKey: String): Promise { + //Query deps.dev GetProject API + const url = `${depsDevAPIRoot}//v3alpha/projects/${projectKey}`; + const response = await fetch(url); + const data = await response.json(); + return DepsDevProjectSchema.parse(data); +} +