7d7ff4475a
* initial esm conversion Signed-off-by: Brian DeHamer <bdehamer@github.com> * esm'ify jest tests Signed-off-by: Brian DeHamer <bdehamer@github.com> * lint issues Signed-off-by: Brian DeHamer <bdehamer@github.com> * debug mock Signed-off-by: Brian DeHamer <bdehamer@github.com> * glob updated Signed-off-by: Brian DeHamer <bdehamer@github.com> * async all file functions Signed-off-by: Brian DeHamer <bdehamer@github.com> * update @actions/github Signed-off-by: Brian DeHamer <bdehamer@github.com> * update @actions/attest Signed-off-by: Brian DeHamer <bdehamer@github.com> * rebuild package-lock.json Signed-off-by: Brian DeHamer <bdehamer@github.com> * use experimental flag for jest in ci Signed-off-by: Brian DeHamer <bdehamer@github.com> * remove stray istanbul ignore Signed-off-by: Brian DeHamer <bdehamer@github.com> * Optimize getSubjectFromPath to avoid concurrent stat calls Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> * Fix boundary condition for MAX_SUBJECT_COUNT check Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> * Improve error message clarity for subject count limit Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> * Update test to match new error message format Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> * rebuild dist Signed-off-by: Brian DeHamer <bdehamer@github.com> * Fix parseSBOMFromPath to check file size before reading Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> * Build package with updated changes Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com> --------- Signed-off-by: Brian DeHamer <bdehamer@github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bdehamer <398027+bdehamer@users.noreply.github.com>
97 lines
2.3 KiB
TypeScript
97 lines
2.3 KiB
TypeScript
import fs from 'fs/promises'
|
|
|
|
import type { Predicate } from '@actions/attest'
|
|
|
|
export type SBOM = {
|
|
type: 'spdx' | 'cyclonedx'
|
|
object: object
|
|
}
|
|
|
|
// SBOMs cannot exceed 16MB.
|
|
const MAX_SBOM_SIZE_BYTES = 16 * 1024 * 1024
|
|
|
|
export const parseSBOMFromPath = async (filePath: string): Promise<SBOM> => {
|
|
let stats
|
|
try {
|
|
stats = await fs.stat(filePath)
|
|
} catch (error) {
|
|
const err = error as NodeJS.ErrnoException
|
|
if (err.code === 'ENOENT') {
|
|
throw new Error('SBOM file not found')
|
|
}
|
|
throw error
|
|
}
|
|
|
|
if (stats.size > MAX_SBOM_SIZE_BYTES) {
|
|
throw new Error(
|
|
`SBOM file exceeds maximum allowed size: ${MAX_SBOM_SIZE_BYTES} bytes`
|
|
)
|
|
}
|
|
|
|
const fileContent = await fs.readFile(filePath, 'utf8')
|
|
const sbom = JSON.parse(fileContent) as object
|
|
|
|
if (checkIsSPDX(sbom)) {
|
|
return { type: 'spdx', object: sbom }
|
|
} else if (checkIsCycloneDX(sbom)) {
|
|
return { type: 'cyclonedx', object: sbom }
|
|
}
|
|
|
|
throw new Error(
|
|
'Unsupported SBOM format. Must be valid SPDX or CycloneDX JSON.'
|
|
)
|
|
}
|
|
|
|
const checkIsSPDX = (sbomObject: {
|
|
spdxVersion?: string
|
|
SPDXID?: string
|
|
}): boolean => {
|
|
return !!(sbomObject?.spdxVersion && sbomObject?.SPDXID)
|
|
}
|
|
|
|
const checkIsCycloneDX = (sbomObject: {
|
|
bomFormat?: string
|
|
serialNumber?: string
|
|
specVersion?: string
|
|
}): boolean => {
|
|
return !!(
|
|
sbomObject?.bomFormat &&
|
|
sbomObject?.serialNumber &&
|
|
sbomObject?.specVersion
|
|
)
|
|
}
|
|
|
|
export const generateSBOMPredicate = (sbom: SBOM): Predicate => {
|
|
switch (sbom.type) {
|
|
case 'spdx':
|
|
return generateSPDXPredicate(sbom.object)
|
|
case 'cyclonedx':
|
|
return generateCycloneDXPredicate(sbom.object)
|
|
default:
|
|
throw new Error('Unsupported SBOM format')
|
|
}
|
|
}
|
|
|
|
// ref: https://github.com/in-toto/attestation/blob/main/spec/predicates/spdx.md
|
|
const generateSPDXPredicate = (sbom: object): Predicate => {
|
|
const spdxVersion = (sbom as { spdxVersion?: string })?.['spdxVersion']
|
|
if (!spdxVersion) {
|
|
throw new Error('Cannot find spdxVersion in the SBOM')
|
|
}
|
|
|
|
const version = spdxVersion.split('-')[1]
|
|
|
|
return {
|
|
type: `https://spdx.dev/Document/v${version}`,
|
|
params: sbom
|
|
}
|
|
}
|
|
|
|
// ref: https://github.com/in-toto/attestation/blob/main/spec/predicates/cyclonedx.md
|
|
const generateCycloneDXPredicate = (sbom: object): Predicate => {
|
|
return {
|
|
type: 'https://cyclonedx.org/bom',
|
|
params: sbom
|
|
}
|
|
}
|