From 4b14eaf21b48cb85814c6aff54432d9c2c46b0ca Mon Sep 17 00:00:00 2001 From: bryancito1090 Date: Wed, 16 Sep 2026 16:54:44 -0500 Subject: [PATCH] feat: implement security agent service with webhook integration and error handling --- .../tokens/security-agent-config.token.ts | 6 +++ .../src/app/domain/models/security.model.ts | 38 +++++++++++++++++++ .../app/domain/ports/security-agent.port.ts | 6 +++ .../src/app/models/security.models.ts | 1 + .../app/services/security-agent.service.ts | 35 +++++++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 src/frontend/src/app/core/tokens/security-agent-config.token.ts create mode 100644 src/frontend/src/app/domain/models/security.model.ts create mode 100644 src/frontend/src/app/domain/ports/security-agent.port.ts create mode 100644 src/frontend/src/app/models/security.models.ts create mode 100644 src/frontend/src/app/services/security-agent.service.ts diff --git a/src/frontend/src/app/core/tokens/security-agent-config.token.ts b/src/frontend/src/app/core/tokens/security-agent-config.token.ts new file mode 100644 index 0000000..a860cc8 --- /dev/null +++ b/src/frontend/src/app/core/tokens/security-agent-config.token.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from '@angular/core'; + +export const SECURITY_AGENT_WEBHOOK_URL = new InjectionToken('SECURITY_AGENT_WEBHOOK_URL', { + providedIn: 'root', + factory: () => 'https://n8n.bryan-bano.com/webhook/security-agent' +}); diff --git a/src/frontend/src/app/domain/models/security.model.ts b/src/frontend/src/app/domain/models/security.model.ts new file mode 100644 index 0000000..cf1bd9d --- /dev/null +++ b/src/frontend/src/app/domain/models/security.model.ts @@ -0,0 +1,38 @@ +export type SimulationMode = 'PURPLE_TEAM' | 'EASM' | 'SOAR'; +export type SecurityVerdict = 'SAFE' | 'ATTENTION_REQUIRED' | 'CRITICAL_THREAT'; + +export interface SecurityAgentParameters { + readonly auto_isolate?: boolean; + readonly scan_depth?: 'quick' | 'full'; +} + +export interface SecurityAgentRequest { + readonly mode: SimulationMode; + readonly target: string; + readonly requested_by: string; + readonly parameters?: SecurityAgentParameters; +} + +export interface SecurityExecutionSummary { + readonly risk_score: number; + readonly verdict: SecurityVerdict; + readonly mitre_techniques: string[]; + readonly containment_executed: boolean; + readonly mitigation_time_ms: number; +} + +export interface SecurityEvidence { + readonly sha256_hash: string; + readonly report_url: string; +} + +export interface SecurityAgentResponse { + readonly success: boolean; + readonly execution_id: string; + readonly simulation_id: string; + readonly timestamp: string; + readonly mode: SimulationMode; + readonly target: string; + readonly summary: SecurityExecutionSummary; + readonly evidence: SecurityEvidence; +} diff --git a/src/frontend/src/app/domain/ports/security-agent.port.ts b/src/frontend/src/app/domain/ports/security-agent.port.ts new file mode 100644 index 0000000..4359bc0 --- /dev/null +++ b/src/frontend/src/app/domain/ports/security-agent.port.ts @@ -0,0 +1,6 @@ +import { Observable } from 'rxjs'; +import { SecurityAgentRequest, SecurityAgentResponse } from '../models/security.model'; + +export abstract class SecurityAgentPort { + abstract triggerSimulation(payload: SecurityAgentRequest): Observable; +} diff --git a/src/frontend/src/app/models/security.models.ts b/src/frontend/src/app/models/security.models.ts new file mode 100644 index 0000000..69b8a91 --- /dev/null +++ b/src/frontend/src/app/models/security.models.ts @@ -0,0 +1 @@ +export * from '../domain/models/security.model'; diff --git a/src/frontend/src/app/services/security-agent.service.ts b/src/frontend/src/app/services/security-agent.service.ts new file mode 100644 index 0000000..2f0e203 --- /dev/null +++ b/src/frontend/src/app/services/security-agent.service.ts @@ -0,0 +1,35 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { SecurityAgentRequest, SecurityAgentResponse } from '../domain/models/security.model'; +import { SecurityAgentPort } from '../domain/ports/security-agent.port'; +import { SECURITY_AGENT_WEBHOOK_URL } from '../core/tokens/security-agent-config.token'; + +@Injectable({ + providedIn: 'root' +}) +export class SecurityAgentService implements SecurityAgentPort { + private readonly http = inject(HttpClient); + private readonly webhookUrl = inject(SECURITY_AGENT_WEBHOOK_URL); + + triggerSimulation(payload: SecurityAgentRequest): Observable { + return this.http.post(this.webhookUrl, payload).pipe( + catchError((error: HttpErrorResponse) => this.handleError(error)) + ); + } + + private handleError(error: HttpErrorResponse): Observable { + let errorMessage = 'Ocurrió un error inesperado al conectar con el orquestador n8n.'; + if (error.status === 429) { + errorMessage = 'Límite de tasa excedido (Rate Limit: máx 10 peticiones cada 5 min).'; + } else if (error.status === 400) { + errorMessage = error.error?.message || 'Payload o parámetros inválidos.'; + } else if (error.status === 0) { + errorMessage = 'Error de red o conexión rechazada con el servidor n8n.'; + } else if (error.status >= 500) { + errorMessage = error.error?.message || `Error interno del orquestador n8n (HTTP ${error.status}).`; + } + return throwError(() => new Error(errorMessage)); + } +}