Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
186 lines
4.8 KiB
TypeScript
186 lines
4.8 KiB
TypeScript
/**
|
|
* Security composable for SBOM and security scanning
|
|
*/
|
|
|
|
import { computed, type ComputedRef, type Ref, ref } from 'vue'
|
|
import { useComplianceStore } from '../plugin'
|
|
import type {
|
|
SBOM,
|
|
SBOMComponent,
|
|
SecurityIssue,
|
|
Vulnerability,
|
|
SecurityScanResult,
|
|
} from '@breakpilot/compliance-sdk-types'
|
|
|
|
export interface UseSecurityReturn {
|
|
// SBOM
|
|
sbom: ComputedRef<SBOM | null>
|
|
components: ComputedRef<SBOMComponent[]>
|
|
componentsByCategory: ComputedRef<Record<string, SBOMComponent[]>>
|
|
vulnerableComponents: ComputedRef<SBOMComponent[]>
|
|
generateSBOM: (source?: string) => Promise<SBOM>
|
|
exportSBOM: (format: 'cyclonedx' | 'spdx') => Promise<Blob>
|
|
|
|
// Security Issues
|
|
securityIssues: ComputedRef<SecurityIssue[]>
|
|
criticalIssues: ComputedRef<SecurityIssue[]>
|
|
openIssues: ComputedRef<SecurityIssue[]>
|
|
issuesBySeverity: ComputedRef<Record<string, SecurityIssue[]>>
|
|
|
|
// Scanning
|
|
scan: (options?: ScanOptions) => Promise<SecurityScanResult>
|
|
lastScanResult: Ref<SecurityScanResult | null>
|
|
isScanning: Ref<boolean>
|
|
|
|
// Vulnerabilities
|
|
vulnerabilities: ComputedRef<Vulnerability[]>
|
|
getVulnerabilitiesForComponent: (componentId: string) => Vulnerability[]
|
|
|
|
// Loading state
|
|
isLoading: Ref<boolean>
|
|
error: Ref<Error | null>
|
|
}
|
|
|
|
export interface ScanOptions {
|
|
tools?: string[]
|
|
targetPath?: string
|
|
excludePaths?: string[]
|
|
}
|
|
|
|
export function useSecurity(): UseSecurityReturn {
|
|
const store = useComplianceStore()
|
|
const { state, security } = store
|
|
|
|
const isLoading = ref(false)
|
|
const isScanning = ref(false)
|
|
const error = ref<Error | null>(null)
|
|
const lastScanResult = ref<SecurityScanResult | null>(null)
|
|
|
|
// SBOM
|
|
const sbom = computed(() => state.sbom)
|
|
|
|
const components = computed(() => state.sbom?.components || [])
|
|
|
|
const componentsByCategory = computed(() => {
|
|
const comps = state.sbom?.components || []
|
|
return comps.reduce(
|
|
(acc, comp) => {
|
|
const cat = comp.category || 'other'
|
|
if (!acc[cat]) acc[cat] = []
|
|
acc[cat].push(comp)
|
|
return acc
|
|
},
|
|
{} as Record<string, SBOMComponent[]>
|
|
)
|
|
})
|
|
|
|
const vulnerableComponents = computed(() => {
|
|
return components.value.filter(c => c.vulnerabilities && c.vulnerabilities.length > 0)
|
|
})
|
|
|
|
const generateSBOM = async (source?: string): Promise<SBOM> => {
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
return await security.generateSBOM(source)
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err : new Error(String(err))
|
|
throw err
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
const exportSBOM = async (format: 'cyclonedx' | 'spdx'): Promise<Blob> => {
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
return await security.exportSBOM(format)
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err : new Error(String(err))
|
|
throw err
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Security Issues
|
|
const securityIssues = computed(() => state.securityIssues)
|
|
|
|
const criticalIssues = computed(() =>
|
|
state.securityIssues.filter(i => i.severity === 'CRITICAL' || i.severity === 'HIGH')
|
|
)
|
|
|
|
const openIssues = computed(() =>
|
|
state.securityIssues.filter(i => i.status === 'OPEN' || i.status === 'IN_PROGRESS')
|
|
)
|
|
|
|
const issuesBySeverity = computed(() => {
|
|
return state.securityIssues.reduce(
|
|
(acc, issue) => {
|
|
if (!acc[issue.severity]) acc[issue.severity] = []
|
|
acc[issue.severity].push(issue)
|
|
return acc
|
|
},
|
|
{} as Record<string, SecurityIssue[]>
|
|
)
|
|
})
|
|
|
|
// Scanning
|
|
const scan = async (options?: ScanOptions): Promise<SecurityScanResult> => {
|
|
isScanning.value = true
|
|
isLoading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const result = await security.scan(options)
|
|
lastScanResult.value = result
|
|
return result
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err : new Error(String(err))
|
|
throw err
|
|
} finally {
|
|
isScanning.value = false
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Vulnerabilities
|
|
const vulnerabilities = computed(() => {
|
|
const vulns: Vulnerability[] = []
|
|
components.value.forEach(comp => {
|
|
if (comp.vulnerabilities) {
|
|
vulns.push(...comp.vulnerabilities)
|
|
}
|
|
})
|
|
return vulns
|
|
})
|
|
|
|
const getVulnerabilitiesForComponent = (componentId: string): Vulnerability[] => {
|
|
const comp = components.value.find(c => c.name === componentId)
|
|
return comp?.vulnerabilities || []
|
|
}
|
|
|
|
return {
|
|
sbom,
|
|
components,
|
|
componentsByCategory,
|
|
vulnerableComponents,
|
|
generateSBOM,
|
|
exportSBOM,
|
|
securityIssues,
|
|
criticalIssues,
|
|
openIssues,
|
|
issuesBySeverity,
|
|
scan,
|
|
lastScanResult,
|
|
isScanning,
|
|
vulnerabilities,
|
|
getVulnerabilitiesForComponent,
|
|
isLoading,
|
|
error,
|
|
}
|
|
}
|