Skip to content

Commit

Permalink
linter
Browse files Browse the repository at this point in the history
Signed-off-by: Amndeep Singh Mann <[email protected]>
  • Loading branch information
Amndeep7 committed Sep 11, 2024
1 parent d3be8d5 commit 3a89ae5
Show file tree
Hide file tree
Showing 2 changed files with 7,219 additions and 5,991 deletions.
231 changes: 151 additions & 80 deletions libs/hdf-converters/src/cyclonedx-sbom-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,49 @@ import {BaseConverter, ILookupPath, MappedTransform} from './base-converter';
import {CweNistMapping} from './mappings/CweNistMapping';
import {filterString, getCCIsForNISTTags} from './utils/global';
import {
CycloneDXSoftwareBillOfMaterialSpecification,
CycloneDXSoftwareBillOfMaterialsStandard,
CycloneDXBillOfMaterialsStandardVulnerability,
CycloneDXSoftwareBillOfMaterialsStandardVulnerability,
FluffyCredits,
PurpleCredits,
FluffyRating,
PurpleRating,
MethodEnum,
Response,
CreationToolsLegacyElement,
ToolsTools,
ToolsToolsLegacy,
FluffyTools,
ComponentClass,
ComponentObject
CycloneDXSoftwareBillOfMaterialSpecification,
CycloneDXSoftwareBillOfMaterialsStandard,
CycloneDXBillOfMaterialsStandardVulnerability,
CycloneDXSoftwareBillOfMaterialsStandardVulnerability,
FluffyCredits,
PurpleCredits,
FluffyRating,
PurpleRating,
MethodEnum,
Response,
CreationToolsLegacyElement,
ToolsTools,
ToolsToolsLegacy,
FluffyTools,
ComponentClass,
ComponentObject
} from '../types/cyclonedx';

const cvssMethods = ["CVSSv2", "CVSSv3", "CVSSv31", "CVSSv4"] as const;
type CVSSMethodEnum = Extract<MethodEnum, typeof cvssMethods[number]>
const cvssMethods = ['CVSSv2', 'CVSSv3', 'CVSSv31', 'CVSSv4'] as const;
type CVSSMethodEnum = Extract<MethodEnum, (typeof cvssMethods)[number]>;

type IntermediaryComponent = Omit<ComponentClass | ComponentObject, 'components'> & {
type IntermediaryComponent = Omit<
ComponentClass | ComponentObject,
'components'
> & {
components?: IntermediaryComponent[];
affectingVulnerabilities?: string[];
isDummy?: boolean;
};

type IntermediaryVulnerability = (CycloneDXBillOfMaterialsStandardVulnerability | CycloneDXSoftwareBillOfMaterialsStandardVulnerability) & {
type IntermediaryVulnerability = (
| CycloneDXBillOfMaterialsStandardVulnerability
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability
) & {
affectedComponents?: number[];
};

type DataStorage = {
components: IntermediaryComponent[];
vulnerabilities: IntermediaryVulnerability[];
raw: CycloneDXSoftwareBillOfMaterialSpecification | CycloneDXSoftwareBillOfMaterialsStandard;
raw:
| CycloneDXSoftwareBillOfMaterialSpecification
| CycloneDXSoftwareBillOfMaterialsStandard;
};

const CWE_NIST_MAPPING = new CweNistMapping();
Expand All @@ -55,12 +63,23 @@ const IMPACT_MAPPING: Map<string, number> = new Map([
]);

// Convert object type to string[] and prepend `CWE` if used directly for tag display
function formatCWETags(input: CycloneDXBillOfMaterialsStandardVulnerability['cwes'] | CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes'], addPrefix = true): string[] {
return input && Array.isArray(input) ? input.map((cwe) => (addPrefix ? `CWE-${cwe}` : `${cwe}`)) : [];
function formatCWETags(
input:
| CycloneDXBillOfMaterialsStandardVulnerability['cwes']
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes'],
addPrefix = true
): string[] {
return input && Array.isArray(input)
? input.map((cwe) => (addPrefix ? `CWE-${cwe}` : `${cwe}`))
: [];
}

// Convert gathered CWEs to corresponding NIST 800-53s
function getNISTTags(input: CycloneDXBillOfMaterialsStandardVulnerability['cwes'] | CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes']): string[] {
function getNISTTags(
input:
| CycloneDXBillOfMaterialsStandardVulnerability['cwes']
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes']
): string[] {
return CWE_NIST_MAPPING.nistFilter(
formatCWETags(input, false),
DEFAULT_NIST_TAG
Expand All @@ -72,13 +91,13 @@ function getNISTTags(input: CycloneDXBillOfMaterialsStandardVulnerability['cwes'
function maxImpact(ratings: FluffyRating[] | PurpleRating[]): number {
return ratings
.map((rating) =>
rating.score && rating.method && cvssMethods.includes(rating.method as CVSSMethodEnum) // cast required since .includes expects the parameter to be a subtype
rating.score &&
rating.method &&
cvssMethods.includes(rating.method as CVSSMethodEnum) // cast required since .includes expects the parameter to be a subtype
? // Prefer to use CVSS-based `score` field when possible
rating.score / 10
: // Else interpret it from `severity` field, defaulting to medium/0.5
IMPACT_MAPPING.get(
rating.severity?.toLowerCase() ?? ''
) ?? 0.5
(IMPACT_MAPPING.get(rating.severity?.toLowerCase() ?? '') ?? 0.5)
)
.reduce(
(maxValue, newValue) =>
Expand Down Expand Up @@ -151,7 +170,9 @@ export class CycloneDXSBOMResults {
// Flatten any arbitrarily nested components list
flattenComponents(data: DataStorage) {
// Pull components from raw data
data.components = _.cloneDeep(data.raw.components) as IntermediaryComponent[];
data.components = _.cloneDeep(
data.raw.components
) as IntermediaryComponent[];

// Look through every component at the top level of the list
for (const component of data.components) {
Expand Down Expand Up @@ -196,7 +217,9 @@ export class CycloneDXSBOMResults {
*/
generateIntermediary(data: DataStorage) {
// Pull vulnerabilities from raw data
data.vulnerabilities = _.cloneDeep(data.raw.vulnerabilities) as IntermediaryVulnerability[];
data.vulnerabilities = _.cloneDeep(
data.raw.vulnerabilities
) as IntermediaryVulnerability[];

for (const vulnerability of data.vulnerabilities) {
vulnerability.affectedComponents = [];
Expand All @@ -205,7 +228,9 @@ export class CycloneDXSBOMResults {
...Array.from(data.components.entries())
// Find every component that is affected via listed bom-refs
.filter(([_index, component]) =>
vulnerability.affects?.map((id) => id.ref.toString()).includes(component['bom-ref'] as string)
vulnerability.affects
?.map((id) => id.ref.toString())
.includes(component['bom-ref'] as string)
)
// Add the index of that affected component to the corresponding vulnerability object
.map(([index, _component]) => index)
Expand All @@ -228,25 +253,25 @@ export class CycloneDXSBOMResults {
formatVEX(data: DataStorage) {
// Pull vulnerabilities from raw data
data.vulnerabilities = [
...(_.cloneDeep(data.raw.vulnerabilities) as CycloneDXBillOfMaterialsStandardVulnerability[] | CycloneDXSoftwareBillOfMaterialsStandardVulnerability[])
...(_.cloneDeep(data.raw.vulnerabilities) as
| CycloneDXBillOfMaterialsStandardVulnerability[]
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability[])
] as unknown as IntermediaryVulnerability[];

for (const vulnerability of data.vulnerabilities) {
vulnerability.affectedComponents = vulnerability.affects?.map(
(id) => {
// Build a dummy component for each bom-ref identified as being affected by the vulnerability
const dummy: IntermediaryComponent = {
name: `${id.ref}`,
'bom-ref': `${id.ref}`,
isDummy: true,
type: "application" // a type must be provided, and "application" is the default classification
};
// Add that component to the corresponding vulnerability object
data.components.push(dummy);
// Return the index of that dummy object
return data.components.length - 1;
}
);
vulnerability.affectedComponents = vulnerability.affects?.map((id) => {
// Build a dummy component for each bom-ref identified as being affected by the vulnerability
const dummy: IntermediaryComponent = {
name: `${id.ref}`,
'bom-ref': `${id.ref}`,
isDummy: true,
type: 'application' // a type must be provided, and "application" is the default classification
};
// Add that component to the corresponding vulnerability object
data.components.push(dummy);
// Return the index of that dummy object
return data.components.length - 1;
});
}
}

Expand Down Expand Up @@ -302,7 +327,9 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
maintainer: {
path: 'raw.metadata.component',
transformer: (input: ComponentClass | ComponentObject): string | undefined => {
transformer: (
input: ComponentClass | ComponentObject
): string | undefined => {
// Find organization of authors if possible
const manufacturer = _.has(input, 'manufacturer')
? ` (${(input.manufacturer as Record<string, unknown>).name})`
Expand Down Expand Up @@ -331,13 +358,22 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
license: {
path: 'raw.metadata.component',
transformer: (input: ComponentClass | ComponentObject): string | undefined => {
if(!input.licenses) {
return undefined;
}
// Certain license reports only provide the license name in the `name` field
// Check there first and then default to `id`
return input.licenses?.map((license) => license?.license?.name ? license.license.name : license?.license?.id).filter(identifier => identifier).join(', ');
transformer: (
input: ComponentClass | ComponentObject
): string | undefined => {
if (!input.licenses) {
return undefined;
}
// Certain license reports only provide the license name in the `name` field
// Check there first and then default to `id`
return input.licenses
?.map((license) =>
license?.license?.name
? license.license.name
: license?.license?.id
)
.filter((identifier) => identifier)
.join(', ');
}
},
supports: [],
Expand All @@ -355,8 +391,11 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
cci: {
path: 'cwes',
transformer: (input: CycloneDXBillOfMaterialsStandardVulnerability['cwes'] | CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes']): string[] =>
getCCIsForNISTTags(getNISTTags(input))
transformer: (
input:
| CycloneDXBillOfMaterialsStandardVulnerability['cwes']
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability['cwes']
): string[] => getCCIsForNISTTags(getNISTTags(input))
},
cwe: {path: 'cwes', transformer: formatCWETags},
'bom-ref': {
Expand All @@ -365,7 +404,9 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
ratings: {
path: 'ratings',
transformer: (input: FluffyRating[] | PurpleRating[]): string | undefined =>
transformer: (
input: FluffyRating[] | PurpleRating[]
): string | undefined =>
input
? [...input]
.map((rating) => {
Expand Down Expand Up @@ -396,22 +437,41 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
credits: {
path: 'credits',
transformer: (input: FluffyCredits | PurpleCredits): string | undefined =>
transformer: (
input: FluffyCredits | PurpleCredits
): string | undefined =>
input
? `${input.individuals?.map((individual) => individual.name).filter(name => name).join(', ')}`
? `${input.individuals
?.map((individual) => individual.name)
.filter((name) => name)
.join(', ')}`
: undefined
},
tools: {
path: 'tools',
transformer: (input: CreationToolsLegacyElement[] | ToolsToolsLegacy[] | ToolsTools | FluffyTools): string | undefined => {
if(!input) {
return undefined;
}
if(Array.isArray(input)) {
return input.map((tool) => tool.name).filter(name => name).join(', ');
}
return [...(input.components?.map(component => component.name) ?? []), ...(input.services?.map(component => component.name) ?? [])].join(', ');
}
transformer: (
input:
| CreationToolsLegacyElement[]
| ToolsToolsLegacy[]
| ToolsTools
| FluffyTools
): string | undefined => {
if (!input) {
return undefined;
}
if (Array.isArray(input)) {
return input
.map((tool) => tool.name)
.filter((name) => name)
.join(', ');
}
return [
...(input.components?.map((component) => component.name) ??
[]),
...(input.services?.map((component) => component.name) ??
[])
].join(', ');
}
},
// Workflow items will not affect `impact`
'analysis.state': {
Expand All @@ -424,12 +484,8 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
},
'analysis.response': {
path: 'analysis.response',
transformer: (
input: Response[]
): string | undefined =>
input && input.length > 0
? input.join(', ')
: undefined
transformer: (input: Response[]): string | undefined =>
input && input.length > 0 ? input.join(', ') : undefined
},
'analysis.detail': {
path: 'analysis.detail',
Expand All @@ -445,7 +501,11 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
}
},
descriptions: {
transformer: (input: CycloneDXBillOfMaterialsStandardVulnerability | CycloneDXSoftwareBillOfMaterialsStandardVulnerability) => {
transformer: (
input:
| CycloneDXBillOfMaterialsStandardVulnerability
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability
) => {
const recommendation = input.recommendation
? `Recommendation: ${input.recommendation}`
: '';
Expand Down Expand Up @@ -489,12 +549,20 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
source_location: {},
title: {
// Give description as title if possible
transformer: (input: CycloneDXBillOfMaterialsStandardVulnerability | CycloneDXSoftwareBillOfMaterialsStandardVulnerability): string =>
transformer: (
input:
| CycloneDXBillOfMaterialsStandardVulnerability
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability
): string =>
input.description ? `${input.description}` : `${input.id}`
},
id: {path: 'id'},
desc: {
transformer: (input: CycloneDXBillOfMaterialsStandardVulnerability | CycloneDXSoftwareBillOfMaterialsStandardVulnerability): string | undefined => {
transformer: (
input:
| CycloneDXBillOfMaterialsStandardVulnerability
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability
): string | undefined => {
const description = input.description
? `Description: ${input.description}`
: '';
Expand All @@ -503,8 +571,11 @@ export class CycloneDXSBOMMapper extends BaseConverter<DataStorage> {
}
},
impact: {
transformer: (input: CycloneDXBillOfMaterialsStandardVulnerability | CycloneDXSoftwareBillOfMaterialsStandardVulnerability): number =>
maxImpact(input.ratings ?? [])
transformer: (
input:
| CycloneDXBillOfMaterialsStandardVulnerability
| CycloneDXSoftwareBillOfMaterialsStandardVulnerability
): number => maxImpact(input.ratings ?? [])
},
code: {
transformer: (vulnerability: Record<string, unknown>): string =>
Expand Down
Loading

0 comments on commit 3a89ae5

Please sign in to comment.