Initial commit: breakpilot-compliance - Compliance SDK Platform
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>
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Compliance Module
|
||||
*
|
||||
* General compliance functionality including controls, evidence,
|
||||
* AI Act, NIS2, and regulatory obligations
|
||||
*/
|
||||
|
||||
import type {
|
||||
Control,
|
||||
Evidence,
|
||||
Requirement,
|
||||
Obligation,
|
||||
AIActResult,
|
||||
AIActRiskCategory,
|
||||
RegulationCode,
|
||||
ComplianceScore,
|
||||
SDKState,
|
||||
RiskSeverity,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import { ComplianceClient } from '../client'
|
||||
|
||||
export class ComplianceModule {
|
||||
private client: ComplianceClient
|
||||
private getState: () => SDKState
|
||||
|
||||
constructor(client: ComplianceClient, getState: () => SDKState) {
|
||||
this.client = client
|
||||
this.getState = getState
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Controls
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getControls(): Control[] {
|
||||
return this.getState().controls
|
||||
}
|
||||
|
||||
getControlById(id: string): Control | undefined {
|
||||
return this.getState().controls.find(c => c.id === id)
|
||||
}
|
||||
|
||||
getControlsByDomain(domain: string): Control[] {
|
||||
return this.getState().controls.filter(c => c.domain === domain)
|
||||
}
|
||||
|
||||
getControlsByStatus(status: string): Control[] {
|
||||
return this.getState().controls.filter(c => c.implementationStatus === status)
|
||||
}
|
||||
|
||||
getControlComplianceRate(): number {
|
||||
const controls = this.getControls()
|
||||
if (controls.length === 0) return 0
|
||||
|
||||
const implemented = controls.filter(c => c.implementationStatus === 'IMPLEMENTED').length
|
||||
return Math.round((implemented / controls.length) * 100)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Evidence
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getEvidence(): Evidence[] {
|
||||
return this.getState().evidence
|
||||
}
|
||||
|
||||
getEvidenceById(id: string): Evidence | undefined {
|
||||
return this.getState().evidence.find(e => e.id === id)
|
||||
}
|
||||
|
||||
getEvidenceByControlId(controlId: string): Evidence[] {
|
||||
return this.getState().evidence.filter(e => e.controlId === controlId)
|
||||
}
|
||||
|
||||
getExpiringEvidence(days: number = 30): Evidence[] {
|
||||
const cutoff = new Date()
|
||||
cutoff.setDate(cutoff.getDate() + days)
|
||||
|
||||
return this.getState().evidence.filter(e => {
|
||||
if (!e.validUntil) return false
|
||||
const validUntil = new Date(e.validUntil)
|
||||
return validUntil <= cutoff && e.status === 'ACTIVE'
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getRequirements(): Requirement[] {
|
||||
return this.getState().requirements
|
||||
}
|
||||
|
||||
getRequirementsByRegulation(regulation: RegulationCode): Requirement[] {
|
||||
return this.getState().requirements.filter(r => r.regulationCode === regulation)
|
||||
}
|
||||
|
||||
getRequirementComplianceRate(regulation?: RegulationCode): number {
|
||||
let requirements = this.getRequirements()
|
||||
if (regulation) {
|
||||
requirements = requirements.filter(r => r.regulationCode === regulation)
|
||||
}
|
||||
if (requirements.length === 0) return 0
|
||||
|
||||
const implemented = requirements.filter(
|
||||
r => r.status === 'IMPLEMENTED' || r.status === 'VERIFIED'
|
||||
).length
|
||||
return Math.round((implemented / requirements.length) * 100)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Obligations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getObligations(): Obligation[] {
|
||||
return this.getState().obligations
|
||||
}
|
||||
|
||||
getUpcomingObligations(days: number = 30): Obligation[] {
|
||||
const cutoff = new Date()
|
||||
cutoff.setDate(cutoff.getDate() + days)
|
||||
|
||||
return this.getState().obligations.filter(o => {
|
||||
if (!o.deadline || o.status === 'COMPLETED') return false
|
||||
const deadline = new Date(o.deadline)
|
||||
return deadline <= cutoff
|
||||
})
|
||||
}
|
||||
|
||||
getOverdueObligations(): Obligation[] {
|
||||
const now = new Date()
|
||||
|
||||
return this.getState().obligations.filter(o => {
|
||||
if (!o.deadline || o.status === 'COMPLETED') return false
|
||||
const deadline = new Date(o.deadline)
|
||||
return deadline < now
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AI Act
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getAIActClassification(): AIActResult | null {
|
||||
return this.getState().aiActClassification
|
||||
}
|
||||
|
||||
getAIActRiskCategory(): AIActRiskCategory | null {
|
||||
return this.getState().aiActClassification?.riskCategory ?? null
|
||||
}
|
||||
|
||||
isHighRiskAI(): boolean {
|
||||
const category = this.getAIActRiskCategory()
|
||||
return category === 'HIGH' || category === 'UNACCEPTABLE'
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compliance Score
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
calculateComplianceScore(): ComplianceScore {
|
||||
const state = this.getState()
|
||||
|
||||
// Calculate overall score based on controls, requirements, and evidence
|
||||
const controlScore = this.getControlComplianceRate()
|
||||
const requirementScore = this.getRequirementComplianceRate()
|
||||
const evidenceCoverage = this.calculateEvidenceCoverage()
|
||||
|
||||
const overall = Math.round((controlScore + requirementScore + evidenceCoverage) / 3)
|
||||
|
||||
// Calculate scores by regulation
|
||||
const byRegulation: Record<string, number> = {}
|
||||
const regulations = new Set(state.requirements.map(r => r.regulationCode))
|
||||
regulations.forEach(reg => {
|
||||
byRegulation[reg] = this.getRequirementComplianceRate(reg as RegulationCode)
|
||||
})
|
||||
|
||||
// Calculate scores by domain
|
||||
const byDomain: Record<string, number> = {}
|
||||
const domains = new Set(state.controls.map(c => c.domain))
|
||||
domains.forEach(domain => {
|
||||
const domainControls = state.controls.filter(c => c.domain === domain)
|
||||
const implemented = domainControls.filter(c => c.implementationStatus === 'IMPLEMENTED').length
|
||||
byDomain[domain] = domainControls.length > 0
|
||||
? Math.round((implemented / domainControls.length) * 100)
|
||||
: 0
|
||||
})
|
||||
|
||||
return {
|
||||
overall,
|
||||
byRegulation: byRegulation as Record<RegulationCode, number>,
|
||||
byDomain: byDomain as Record<string, number>,
|
||||
trend: 'STABLE', // Would need historical data to calculate
|
||||
lastCalculated: new Date(),
|
||||
}
|
||||
}
|
||||
|
||||
private calculateEvidenceCoverage(): number {
|
||||
const controls = this.getControls()
|
||||
const implementedControls = controls.filter(c => c.implementationStatus === 'IMPLEMENTED')
|
||||
|
||||
if (implementedControls.length === 0) return 0
|
||||
|
||||
const controlsWithEvidence = implementedControls.filter(c => {
|
||||
const evidence = this.getEvidenceByControlId(c.id)
|
||||
return evidence.some(e => e.status === 'ACTIVE')
|
||||
})
|
||||
|
||||
return Math.round((controlsWithEvidence.length / implementedControls.length) * 100)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Risks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getRisks() {
|
||||
return this.getState().risks
|
||||
}
|
||||
|
||||
getRisksByStatus(status: string) {
|
||||
return this.getRisks().filter(r => r.status === status)
|
||||
}
|
||||
|
||||
getRisksBySeverity(severity: RiskSeverity) {
|
||||
return this.getRisks().filter(r => r.severity === severity)
|
||||
}
|
||||
|
||||
getCriticalRisks() {
|
||||
return this.getRisks().filter(r => r.severity === 'CRITICAL' || r.severity === 'HIGH')
|
||||
}
|
||||
|
||||
getAverageRiskScore(): number {
|
||||
const risks = this.getRisks()
|
||||
if (risks.length === 0) return 0
|
||||
|
||||
const totalScore = risks.reduce((sum, r) => sum + r.residualRiskScore, 0)
|
||||
return Math.round(totalScore / risks.length)
|
||||
}
|
||||
}
|
||||
|
||||
export function createComplianceModule(
|
||||
client: ComplianceClient,
|
||||
getState: () => SDKState
|
||||
): ComplianceModule {
|
||||
return new ComplianceModule(client, getState)
|
||||
}
|
||||
155
breakpilot-compliance-sdk/packages/core/src/modules/dsgvo.ts
Normal file
155
breakpilot-compliance-sdk/packages/core/src/modules/dsgvo.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* DSGVO Module
|
||||
*
|
||||
* GDPR compliance functionality
|
||||
*/
|
||||
|
||||
import type {
|
||||
DSRRequest,
|
||||
DSRRequestType,
|
||||
ConsentRecord,
|
||||
ConsentPurpose,
|
||||
ProcessingActivity,
|
||||
DSFA,
|
||||
TOM,
|
||||
RetentionPolicy,
|
||||
CookieBannerConfig,
|
||||
SDKState,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import { ComplianceClient } from '../client'
|
||||
|
||||
export class DSGVOModule {
|
||||
private client: ComplianceClient
|
||||
private getState: () => SDKState
|
||||
|
||||
constructor(client: ComplianceClient, getState: () => SDKState) {
|
||||
this.client = client
|
||||
this.getState = getState
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DSR (Data Subject Requests)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async submitDSR(type: DSRRequestType, requesterEmail: string, requesterName: string): Promise<DSRRequest> {
|
||||
const response = await fetch(`${this.client.getTenantId()}/dsgvo/dsr`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type, requesterEmail, requesterName }),
|
||||
})
|
||||
return response.json()
|
||||
}
|
||||
|
||||
getDSRRequests(): DSRRequest[] {
|
||||
return this.getState().dsrRequests
|
||||
}
|
||||
|
||||
getDSRById(id: string): DSRRequest | undefined {
|
||||
return this.getState().dsrRequests.find(r => r.id === id)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Consent Management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getConsents(): ConsentRecord[] {
|
||||
return this.getState().consents
|
||||
}
|
||||
|
||||
getConsentsByUserId(userId: string): ConsentRecord[] {
|
||||
return this.getState().consents.filter(c => c.userId === userId)
|
||||
}
|
||||
|
||||
hasConsent(userId: string, purpose: ConsentPurpose): boolean {
|
||||
const consents = this.getConsentsByUserId(userId)
|
||||
const latestConsent = consents
|
||||
.filter(c => c.consentType === purpose)
|
||||
.sort((a, b) => new Date(b.grantedAt).getTime() - new Date(a.grantedAt).getTime())[0]
|
||||
|
||||
return latestConsent?.granted && !latestConsent.revokedAt
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// VVT (Processing Register)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getProcessingActivities(): ProcessingActivity[] {
|
||||
return this.getState().vvt
|
||||
}
|
||||
|
||||
getProcessingActivityById(id: string): ProcessingActivity | undefined {
|
||||
return this.getState().vvt.find(p => p.id === id)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DSFA
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getDSFA(): DSFA | null {
|
||||
return this.getState().dsfa
|
||||
}
|
||||
|
||||
isDSFARequired(): boolean {
|
||||
const state = this.getState()
|
||||
const activeUseCase = state.useCases.find(uc => uc.id === state.activeUseCase)
|
||||
return activeUseCase?.assessmentResult?.dsfaRequired ?? false
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TOMs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getTOMs(): TOM[] {
|
||||
return this.getState().toms
|
||||
}
|
||||
|
||||
getTOMsByCategory(category: string): TOM[] {
|
||||
return this.getState().toms.filter(t => t.category === category)
|
||||
}
|
||||
|
||||
getTOMScore(): number {
|
||||
const toms = this.getTOMs()
|
||||
if (toms.length === 0) return 0
|
||||
|
||||
const implemented = toms.filter(t => t.implementationStatus === 'IMPLEMENTED').length
|
||||
return Math.round((implemented / toms.length) * 100)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Retention Policies
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getRetentionPolicies(): RetentionPolicy[] {
|
||||
return this.getState().retentionPolicies
|
||||
}
|
||||
|
||||
getUpcomingDeletions(days: number = 30): RetentionPolicy[] {
|
||||
const cutoff = new Date()
|
||||
cutoff.setDate(cutoff.getDate() + days)
|
||||
|
||||
return this.getState().retentionPolicies.filter(p => {
|
||||
const nextReview = new Date(p.nextReviewDate)
|
||||
return nextReview <= cutoff
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cookie Banner
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getCookieBannerConfig(): CookieBannerConfig | null {
|
||||
return this.getState().cookieBanner
|
||||
}
|
||||
|
||||
async generateCookieBannerCode(): Promise<{ html: string; css: string; js: string } | null> {
|
||||
const config = this.getCookieBannerConfig()
|
||||
if (!config) return null
|
||||
|
||||
const response = await this.client.generateDocument('cookie_banner', { config })
|
||||
return response.content ? JSON.parse(response.content) : null
|
||||
}
|
||||
}
|
||||
|
||||
export function createDSGVOModule(client: ComplianceClient, getState: () => SDKState): DSGVOModule {
|
||||
return new DSGVOModule(client, getState)
|
||||
}
|
||||
206
breakpilot-compliance-sdk/packages/core/src/modules/rag.ts
Normal file
206
breakpilot-compliance-sdk/packages/core/src/modules/rag.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* RAG Module
|
||||
*
|
||||
* Legal RAG system for semantic search and AI-powered legal assistance
|
||||
*/
|
||||
|
||||
import type {
|
||||
SearchQuery,
|
||||
SearchResponse,
|
||||
AssistantQuery,
|
||||
AssistantResponse,
|
||||
LegalDocument,
|
||||
ChatSession,
|
||||
ChatMessage,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import { ComplianceClient } from '../client'
|
||||
|
||||
export class RAGModule {
|
||||
private client: ComplianceClient
|
||||
private chatHistory: ChatMessage[] = []
|
||||
private sessionId: string | null = null
|
||||
|
||||
constructor(client: ComplianceClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Search
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async search(query: string, options?: Partial<SearchQuery>): Promise<SearchResponse> {
|
||||
const searchRequest: SearchQuery = {
|
||||
query,
|
||||
limit: options?.limit ?? 10,
|
||||
offset: options?.offset ?? 0,
|
||||
filters: options?.filters,
|
||||
includeMetadata: options?.includeMetadata ?? true,
|
||||
scoreThreshold: options?.scoreThreshold ?? 0.5,
|
||||
}
|
||||
|
||||
return this.client.searchRAG({
|
||||
query: searchRequest.query,
|
||||
filters: searchRequest.filters,
|
||||
limit: searchRequest.limit,
|
||||
offset: searchRequest.offset,
|
||||
})
|
||||
}
|
||||
|
||||
async searchByRegulation(regulation: string, query: string): Promise<SearchResponse> {
|
||||
return this.search(query, {
|
||||
filters: {
|
||||
documentCodes: [regulation],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async searchByArticle(regulation: string, article: string): Promise<SearchResponse> {
|
||||
return this.search(`${regulation} Artikel ${article}`, {
|
||||
filters: {
|
||||
documentCodes: [regulation],
|
||||
articles: [article],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Legal Assistant
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async ask(question: string, options?: Partial<AssistantQuery>): Promise<AssistantResponse> {
|
||||
const request: AssistantQuery = {
|
||||
question,
|
||||
context: options?.context,
|
||||
documents: options?.documents,
|
||||
maxSources: options?.maxSources ?? 5,
|
||||
language: options?.language ?? 'de',
|
||||
responseFormat: options?.responseFormat ?? 'detailed',
|
||||
}
|
||||
|
||||
const response = await this.client.askRAG({
|
||||
question: request.question,
|
||||
context: request.context,
|
||||
documents: request.documents,
|
||||
maxSources: request.maxSources,
|
||||
language: request.language,
|
||||
})
|
||||
|
||||
// Add to chat history
|
||||
this.chatHistory.push({
|
||||
id: `user-${Date.now()}`,
|
||||
role: 'user',
|
||||
content: question,
|
||||
timestamp: new Date(),
|
||||
})
|
||||
|
||||
this.chatHistory.push({
|
||||
id: `assistant-${Date.now()}`,
|
||||
role: 'assistant',
|
||||
content: response.answer,
|
||||
timestamp: new Date(),
|
||||
sources: response.sources,
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
async askAboutRegulation(regulation: string, question: string): Promise<AssistantResponse> {
|
||||
return this.ask(question, {
|
||||
documents: [regulation],
|
||||
context: `Kontext: Frage bezieht sich auf ${regulation}`,
|
||||
})
|
||||
}
|
||||
|
||||
async explainArticle(regulation: string, article: string): Promise<AssistantResponse> {
|
||||
return this.ask(`Erkläre ${regulation} Artikel ${article} einfach und verständlich.`, {
|
||||
documents: [regulation],
|
||||
})
|
||||
}
|
||||
|
||||
async checkCompliance(
|
||||
regulation: string,
|
||||
scenario: string
|
||||
): Promise<AssistantResponse> {
|
||||
return this.ask(
|
||||
`Prüfe folgendes Szenario auf Compliance mit ${regulation}: ${scenario}`,
|
||||
{
|
||||
documents: [regulation],
|
||||
responseFormat: 'detailed',
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Chat Session
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
startNewSession(): void {
|
||||
this.sessionId = `session-${Date.now()}`
|
||||
this.chatHistory = []
|
||||
}
|
||||
|
||||
getChatHistory(): ChatMessage[] {
|
||||
return [...this.chatHistory]
|
||||
}
|
||||
|
||||
clearChatHistory(): void {
|
||||
this.chatHistory = []
|
||||
}
|
||||
|
||||
getSessionId(): string | null {
|
||||
return this.sessionId
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Document Info
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getAvailableRegulations(): readonly string[] {
|
||||
return [
|
||||
'DSGVO',
|
||||
'AI_ACT',
|
||||
'NIS2',
|
||||
'EPRIVACY',
|
||||
'TDDDG',
|
||||
'SCC',
|
||||
'DPF',
|
||||
'CRA',
|
||||
'EUCSA',
|
||||
'DATA_ACT',
|
||||
'DGA',
|
||||
'DSA',
|
||||
'EAA',
|
||||
'BDSG',
|
||||
'ISO_27001',
|
||||
'BSI_GRUNDSCHUTZ',
|
||||
'KRITIS',
|
||||
'BAIT',
|
||||
'VAIT',
|
||||
'SOC2',
|
||||
'PCI_DSS',
|
||||
] as const
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Quick Actions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async getQuickAnswer(question: string): Promise<string> {
|
||||
const response = await this.ask(question, {
|
||||
responseFormat: 'concise',
|
||||
maxSources: 3,
|
||||
})
|
||||
return response.answer
|
||||
}
|
||||
|
||||
async findRelevantArticles(topic: string): Promise<SearchResponse> {
|
||||
return this.search(topic, {
|
||||
limit: 5,
|
||||
scoreThreshold: 0.7,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function createRAGModule(client: ComplianceClient): RAGModule {
|
||||
return new RAGModule(client)
|
||||
}
|
||||
241
breakpilot-compliance-sdk/packages/core/src/modules/security.ts
Normal file
241
breakpilot-compliance-sdk/packages/core/src/modules/security.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Security Module
|
||||
*
|
||||
* Security scanning and SBOM management
|
||||
*/
|
||||
|
||||
import type {
|
||||
SBOM,
|
||||
SecurityIssue,
|
||||
SecurityScanResult,
|
||||
BacklogItem,
|
||||
SecurityIssueSeverity,
|
||||
SecurityTool,
|
||||
FindingsSummary,
|
||||
SDKState,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import { ComplianceClient } from '../client'
|
||||
|
||||
export class SecurityModule {
|
||||
private client: ComplianceClient
|
||||
private getState: () => SDKState
|
||||
|
||||
constructor(client: ComplianceClient, getState: () => SDKState) {
|
||||
this.client = client
|
||||
this.getState = getState
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Security Scanning
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async startScan(options?: {
|
||||
tools?: SecurityTool[]
|
||||
targetPath?: string
|
||||
severityThreshold?: SecurityIssueSeverity
|
||||
generateSBOM?: boolean
|
||||
}): Promise<{ id: string; status: string }> {
|
||||
return this.client.startSecurityScan({
|
||||
tools: options?.tools,
|
||||
targetPath: options?.targetPath,
|
||||
severityThreshold: options?.severityThreshold,
|
||||
generateSBOM: options?.generateSBOM ?? true,
|
||||
})
|
||||
}
|
||||
|
||||
async getScanResult(scanId: string): Promise<SecurityScanResult | null> {
|
||||
const result = await this.client.getSecurityScanResult(scanId)
|
||||
return result as SecurityScanResult | null
|
||||
}
|
||||
|
||||
getLastScanResult(): SecurityScanResult | null {
|
||||
const screening = this.getState().screening
|
||||
return screening?.securityScan ?? null
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SBOM
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getSBOM(): SBOM | null {
|
||||
return this.getState().sbom
|
||||
}
|
||||
|
||||
getComponents() {
|
||||
return this.getSBOM()?.components ?? []
|
||||
}
|
||||
|
||||
getComponentsByLicense(license: string) {
|
||||
return this.getComponents().filter(c => c.licenses.includes(license as never))
|
||||
}
|
||||
|
||||
getVulnerableComponents() {
|
||||
return this.getComponents().filter(c => c.vulnerabilities.length > 0)
|
||||
}
|
||||
|
||||
getLicenseSummary(): Record<string, number> {
|
||||
const components = this.getComponents()
|
||||
const summary: Record<string, number> = {}
|
||||
|
||||
components.forEach(c => {
|
||||
c.licenses.forEach(license => {
|
||||
summary[license] = (summary[license] || 0) + 1
|
||||
})
|
||||
})
|
||||
|
||||
return summary
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Security Issues
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getSecurityIssues(): SecurityIssue[] {
|
||||
return this.getState().securityIssues
|
||||
}
|
||||
|
||||
getIssueById(id: string): SecurityIssue | undefined {
|
||||
return this.getSecurityIssues().find(i => i.id === id)
|
||||
}
|
||||
|
||||
getIssuesBySeverity(severity: SecurityIssueSeverity): SecurityIssue[] {
|
||||
return this.getSecurityIssues().filter(i => i.severity === severity)
|
||||
}
|
||||
|
||||
getIssuesByStatus(status: string): SecurityIssue[] {
|
||||
return this.getSecurityIssues().filter(i => i.status === status)
|
||||
}
|
||||
|
||||
getIssuesByTool(tool: SecurityTool): SecurityIssue[] {
|
||||
return this.getSecurityIssues().filter(i => i.tool === tool)
|
||||
}
|
||||
|
||||
getOpenIssues(): SecurityIssue[] {
|
||||
return this.getSecurityIssues().filter(i => i.status === 'OPEN' || i.status === 'IN_PROGRESS')
|
||||
}
|
||||
|
||||
getCriticalIssues(): SecurityIssue[] {
|
||||
return this.getSecurityIssues().filter(
|
||||
i => (i.severity === 'CRITICAL' || i.severity === 'HIGH') && i.status === 'OPEN'
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Backlog
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getBacklog(): BacklogItem[] {
|
||||
return this.getState().securityBacklog
|
||||
}
|
||||
|
||||
getBacklogByStatus(status: 'OPEN' | 'IN_PROGRESS' | 'DONE'): BacklogItem[] {
|
||||
return this.getBacklog().filter(i => i.status === status)
|
||||
}
|
||||
|
||||
getOverdueBacklogItems(): BacklogItem[] {
|
||||
const now = new Date()
|
||||
return this.getBacklog().filter(i => {
|
||||
if (!i.dueDate || i.status === 'DONE') return false
|
||||
return new Date(i.dueDate) < now
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Summary & Statistics
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getSecuritySummary(): FindingsSummary {
|
||||
const issues = this.getSecurityIssues()
|
||||
|
||||
const bySeverity: Record<SecurityIssueSeverity, number> = {
|
||||
CRITICAL: 0,
|
||||
HIGH: 0,
|
||||
MEDIUM: 0,
|
||||
LOW: 0,
|
||||
INFO: 0,
|
||||
}
|
||||
|
||||
const byStatus: Record<string, number> = {
|
||||
OPEN: 0,
|
||||
IN_PROGRESS: 0,
|
||||
RESOLVED: 0,
|
||||
ACCEPTED: 0,
|
||||
FALSE_POSITIVE: 0,
|
||||
}
|
||||
|
||||
const byTool: Record<string, number> = {}
|
||||
|
||||
issues.forEach(issue => {
|
||||
bySeverity[issue.severity]++
|
||||
byStatus[issue.status]++
|
||||
byTool[issue.tool] = (byTool[issue.tool] || 0) + 1
|
||||
})
|
||||
|
||||
// Calculate average resolution time for resolved issues
|
||||
const resolvedIssues = issues.filter(i => i.status === 'RESOLVED' && i.resolvedAt)
|
||||
let averageResolutionDays = 0
|
||||
if (resolvedIssues.length > 0) {
|
||||
const totalDays = resolvedIssues.reduce((sum, issue) => {
|
||||
// Would need createdAt field to calculate properly
|
||||
return sum + 7 // Placeholder
|
||||
}, 0)
|
||||
averageResolutionDays = Math.round(totalDays / resolvedIssues.length)
|
||||
}
|
||||
|
||||
// Find oldest unresolved issue
|
||||
const openIssues = issues.filter(i => i.status === 'OPEN')
|
||||
let oldestUnresolvedDays = 0
|
||||
// Would need createdAt field to calculate properly
|
||||
|
||||
return {
|
||||
totalFindings: issues.length,
|
||||
bySeverity,
|
||||
byStatus: byStatus as Record<string, number>,
|
||||
byTool: byTool as Record<SecurityTool, number>,
|
||||
averageResolutionDays,
|
||||
oldestUnresolvedDays,
|
||||
}
|
||||
}
|
||||
|
||||
getSecurityScore(): number {
|
||||
const issues = this.getSecurityIssues()
|
||||
const openIssues = issues.filter(i => i.status === 'OPEN' || i.status === 'IN_PROGRESS')
|
||||
|
||||
if (issues.length === 0) return 100
|
||||
|
||||
// Weight by severity
|
||||
const severityWeights = {
|
||||
CRITICAL: 10,
|
||||
HIGH: 5,
|
||||
MEDIUM: 2,
|
||||
LOW: 1,
|
||||
INFO: 0,
|
||||
}
|
||||
|
||||
const totalWeight = issues.reduce((sum, i) => sum + severityWeights[i.severity], 0)
|
||||
const openWeight = openIssues.reduce((sum, i) => sum + severityWeights[i.severity], 0)
|
||||
|
||||
if (totalWeight === 0) return 100
|
||||
|
||||
return Math.round(((totalWeight - openWeight) / totalWeight) * 100)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getAvailableTools(): SecurityTool[] {
|
||||
return ['gitleaks', 'semgrep', 'bandit', 'trivy', 'grype', 'syft']
|
||||
}
|
||||
|
||||
async exportSBOM(format: 'CycloneDX' | 'SPDX'): Promise<Blob> {
|
||||
return this.client.exportState(format === 'CycloneDX' ? 'json' : 'json')
|
||||
}
|
||||
}
|
||||
|
||||
export function createSecurityModule(
|
||||
client: ComplianceClient,
|
||||
getState: () => SDKState
|
||||
): SecurityModule {
|
||||
return new SecurityModule(client, getState)
|
||||
}
|
||||
Reference in New Issue
Block a user