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>
259 lines
7.2 KiB
TypeScript
259 lines
7.2 KiB
TypeScript
/**
|
|
* Controls composable for compliance controls management
|
|
*/
|
|
|
|
import { computed, type ComputedRef, type Ref, ref } from 'vue'
|
|
import { useComplianceStore } from '../plugin'
|
|
import type {
|
|
Control,
|
|
Evidence,
|
|
Risk,
|
|
ControlDomain,
|
|
ImplementationStatus,
|
|
EvidenceType,
|
|
RiskLikelihood,
|
|
RiskImpact,
|
|
} from '@breakpilot/compliance-sdk-types'
|
|
|
|
export interface UseControlsReturn {
|
|
// Controls
|
|
controls: ComputedRef<Control[]>
|
|
controlsByDomain: ComputedRef<Record<ControlDomain, Control[]>>
|
|
implementedControls: ComputedRef<Control[]>
|
|
pendingControls: ComputedRef<Control[]>
|
|
getControl: (id: string) => Control | undefined
|
|
addControl: (control: Omit<Control, 'id' | 'createdAt' | 'updatedAt'>) => void
|
|
updateControl: (id: string, updates: Partial<Control>) => void
|
|
deleteControl: (id: string) => void
|
|
setControlStatus: (id: string, status: ImplementationStatus) => void
|
|
|
|
// Evidence
|
|
evidence: ComputedRef<Evidence[]>
|
|
validEvidence: ComputedRef<Evidence[]>
|
|
expiredEvidence: ComputedRef<Evidence[]>
|
|
getEvidenceForControl: (controlId: string) => Evidence[]
|
|
addEvidence: (evidence: Omit<Evidence, 'id' | 'uploadedAt'>) => void
|
|
updateEvidence: (id: string, updates: Partial<Evidence>) => void
|
|
deleteEvidence: (id: string) => void
|
|
|
|
// Risks
|
|
risks: ComputedRef<Risk[]>
|
|
criticalRisks: ComputedRef<Risk[]>
|
|
unmitigatedRisks: ComputedRef<Risk[]>
|
|
getRisksForControl: (controlId: string) => Risk[]
|
|
addRisk: (risk: Omit<Risk, 'id' | 'createdAt' | 'updatedAt'>) => void
|
|
updateRisk: (id: string, updates: Partial<Risk>) => void
|
|
deleteRisk: (id: string) => void
|
|
assessRisk: (id: string, likelihood: RiskLikelihood, impact: RiskImpact) => void
|
|
|
|
// Compliance Score
|
|
complianceScore: ComputedRef<number>
|
|
scoreByDomain: ComputedRef<Record<ControlDomain, number>>
|
|
}
|
|
|
|
export function useControls(): UseControlsReturn {
|
|
const store = useComplianceStore()
|
|
const { state, dispatch, compliance } = store
|
|
|
|
// Controls
|
|
const controls = computed(() => state.controls)
|
|
|
|
const controlsByDomain = computed(() => {
|
|
const domains: ControlDomain[] = [
|
|
'ACCESS_CONTROL',
|
|
'DATA_PROTECTION',
|
|
'NETWORK_SECURITY',
|
|
'INCIDENT_RESPONSE',
|
|
'BUSINESS_CONTINUITY',
|
|
'COMPLIANCE',
|
|
'RISK_MANAGEMENT',
|
|
'ASSET_MANAGEMENT',
|
|
'HUMAN_RESOURCES',
|
|
]
|
|
|
|
return domains.reduce(
|
|
(acc, domain) => {
|
|
acc[domain] = state.controls.filter(c => c.domain === domain)
|
|
return acc
|
|
},
|
|
{} as Record<ControlDomain, Control[]>
|
|
)
|
|
})
|
|
|
|
const implementedControls = computed(() =>
|
|
state.controls.filter(c => c.implementationStatus === 'IMPLEMENTED')
|
|
)
|
|
|
|
const pendingControls = computed(() =>
|
|
state.controls.filter(c => c.implementationStatus === 'NOT_IMPLEMENTED')
|
|
)
|
|
|
|
const getControl = (id: string): Control | undefined => {
|
|
return state.controls.find(c => c.id === id)
|
|
}
|
|
|
|
const addControl = (control: Omit<Control, 'id' | 'createdAt' | 'updatedAt'>): void => {
|
|
dispatch({ type: 'ADD_CONTROL', payload: control })
|
|
}
|
|
|
|
const updateControl = (id: string, updates: Partial<Control>): void => {
|
|
dispatch({ type: 'UPDATE_CONTROL', payload: { id, updates } })
|
|
}
|
|
|
|
const deleteControl = (id: string): void => {
|
|
dispatch({ type: 'DELETE_CONTROL', payload: id })
|
|
}
|
|
|
|
const setControlStatus = (id: string, status: ImplementationStatus): void => {
|
|
dispatch({
|
|
type: 'UPDATE_CONTROL',
|
|
payload: { id, updates: { implementationStatus: status } },
|
|
})
|
|
}
|
|
|
|
// Evidence
|
|
const evidence = computed(() => state.evidence)
|
|
|
|
const validEvidence = computed(() => {
|
|
const now = new Date()
|
|
return state.evidence.filter(e => !e.validUntil || new Date(e.validUntil) > now)
|
|
})
|
|
|
|
const expiredEvidence = computed(() => {
|
|
const now = new Date()
|
|
return state.evidence.filter(e => e.validUntil && new Date(e.validUntil) <= now)
|
|
})
|
|
|
|
const getEvidenceForControl = (controlId: string): Evidence[] => {
|
|
return state.evidence.filter(e => e.controlIds.includes(controlId))
|
|
}
|
|
|
|
const addEvidence = (ev: Omit<Evidence, 'id' | 'uploadedAt'>): void => {
|
|
dispatch({ type: 'ADD_EVIDENCE', payload: ev })
|
|
}
|
|
|
|
const updateEvidence = (id: string, updates: Partial<Evidence>): void => {
|
|
dispatch({ type: 'UPDATE_EVIDENCE', payload: { id, updates } })
|
|
}
|
|
|
|
const deleteEvidence = (id: string): void => {
|
|
dispatch({ type: 'DELETE_EVIDENCE', payload: id })
|
|
}
|
|
|
|
// Risks
|
|
const risks = computed(() => state.risks)
|
|
|
|
const criticalRisks = computed(() => compliance.getCriticalRisks())
|
|
|
|
const unmitigatedRisks = computed(() =>
|
|
state.risks.filter(r => r.status === 'IDENTIFIED' || r.status === 'ANALYZED')
|
|
)
|
|
|
|
const getRisksForControl = (controlId: string): Risk[] => {
|
|
return state.risks.filter(r => r.controlIds?.includes(controlId))
|
|
}
|
|
|
|
const addRisk = (risk: Omit<Risk, 'id' | 'createdAt' | 'updatedAt'>): void => {
|
|
dispatch({ type: 'ADD_RISK', payload: risk })
|
|
}
|
|
|
|
const updateRisk = (id: string, updates: Partial<Risk>): void => {
|
|
dispatch({ type: 'UPDATE_RISK', payload: { id, updates } })
|
|
}
|
|
|
|
const deleteRisk = (id: string): void => {
|
|
dispatch({ type: 'DELETE_RISK', payload: id })
|
|
}
|
|
|
|
const assessRisk = (id: string, likelihood: RiskLikelihood, impact: RiskImpact): void => {
|
|
const severity = likelihood * impact
|
|
let severityLevel: Risk['severity']
|
|
|
|
if (severity >= 20) severityLevel = 'CRITICAL'
|
|
else if (severity >= 12) severityLevel = 'HIGH'
|
|
else if (severity >= 6) severityLevel = 'MEDIUM'
|
|
else severityLevel = 'LOW'
|
|
|
|
dispatch({
|
|
type: 'UPDATE_RISK',
|
|
payload: {
|
|
id,
|
|
updates: {
|
|
likelihood,
|
|
impact,
|
|
severity: severityLevel,
|
|
status: 'ANALYZED',
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Compliance Score
|
|
const complianceScore = computed(() => {
|
|
const score = compliance.calculateComplianceScore()
|
|
return score.overall
|
|
})
|
|
|
|
const scoreByDomain = computed(() => {
|
|
const domains: ControlDomain[] = [
|
|
'ACCESS_CONTROL',
|
|
'DATA_PROTECTION',
|
|
'NETWORK_SECURITY',
|
|
'INCIDENT_RESPONSE',
|
|
'BUSINESS_CONTINUITY',
|
|
'COMPLIANCE',
|
|
'RISK_MANAGEMENT',
|
|
'ASSET_MANAGEMENT',
|
|
'HUMAN_RESOURCES',
|
|
]
|
|
|
|
return domains.reduce(
|
|
(acc, domain) => {
|
|
const domainControls = state.controls.filter(c => c.domain === domain)
|
|
if (domainControls.length === 0) {
|
|
acc[domain] = 0
|
|
return acc
|
|
}
|
|
|
|
const implemented = domainControls.filter(c => c.implementationStatus === 'IMPLEMENTED')
|
|
const partial = domainControls.filter(c => c.implementationStatus === 'PARTIAL')
|
|
|
|
acc[domain] = Math.round(
|
|
((implemented.length + partial.length * 0.5) / domainControls.length) * 100
|
|
)
|
|
return acc
|
|
},
|
|
{} as Record<ControlDomain, number>
|
|
)
|
|
})
|
|
|
|
return {
|
|
controls,
|
|
controlsByDomain,
|
|
implementedControls,
|
|
pendingControls,
|
|
getControl,
|
|
addControl,
|
|
updateControl,
|
|
deleteControl,
|
|
setControlStatus,
|
|
evidence,
|
|
validEvidence,
|
|
expiredEvidence,
|
|
getEvidenceForControl,
|
|
addEvidence,
|
|
updateEvidence,
|
|
deleteEvidence,
|
|
risks,
|
|
criticalRisks,
|
|
unmitigatedRisks,
|
|
getRisksForControl,
|
|
addRisk,
|
|
updateRisk,
|
|
deleteRisk,
|
|
assessRisk,
|
|
complianceScore,
|
|
scoreByDomain,
|
|
}
|
|
}
|