Security hardening and edge case fixes across frontend

Security Improvements:
- Add input sanitization utilities (XSS, SQL injection prevention)
- Add token validation with JWT structure verification
- Add secure form validators with pattern enforcement
- Implement proper token storage with encryption support

Service Hardening:
- Add timeout (30s) and retry logic (3 attempts) to all API calls
- Add UUID validation for all ID parameters
- Add null/undefined checks with defensive defaults
- Proper error propagation with typed error handling

Component Fixes:
- Fix memory leaks with takeUntilDestroyed pattern
- Remove mock data fallbacks in error handlers
- Add proper loading/error state management
- Add form field length limits and validation

Files affected: 51 (6000+ lines added for security)
This commit is contained in:
Mahi
2026-02-08 02:10:09 -04:00
parent 80566bf0a2
commit 2c10cd5662
51 changed files with 6094 additions and 656 deletions

View File

@@ -1,4 +1,5 @@
import { Component, OnInit, inject, signal } from '@angular/core';
import { Component, OnInit, OnDestroy, inject, signal, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { MatCardModule } from '@angular/material/card';
@@ -128,12 +129,14 @@ import { WebhookLogEntryDto } from '../../../api/models';
`,
],
})
export class WebhookLogsComponent implements OnInit {
export class WebhookLogsComponent implements OnInit, OnDestroy {
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly webhookService = inject(WebhookService);
private readonly destroyRef = inject(DestroyRef);
readonly loading = signal(true);
readonly hasError = signal(false);
readonly logs = signal<WebhookLogEntryDto[]>([]);
readonly totalItems = signal(0);
readonly pageSize = signal(20);
@@ -156,15 +159,19 @@ export class WebhookLogsComponent implements OnInit {
if (!this.webhookId) return;
this.loading.set(true);
this.hasError.set(false);
this.webhookService
.getWebhookLogs(this.webhookId, this.pageIndex() + 1, this.pageSize())
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (response) => {
this.logs.set(response.data);
this.totalItems.set(response.total);
this.logs.set(response?.data ?? []);
this.totalItems.set(response?.total ?? 0);
this.loading.set(false);
},
error: () => {
this.hasError.set(true);
this.loading.set(false);
},
});
@@ -177,10 +184,19 @@ export class WebhookLogsComponent implements OnInit {
}
formatEvent(event: string): string {
return event.replace(/_/g, ' ').toLowerCase();
return event?.replace(/_/g, ' ').toLowerCase() ?? '';
}
isSuccess(statusCode: number): boolean {
return statusCode >= 200 && statusCode < 300;
}
ngOnDestroy(): void {
// Cleanup handled by DestroyRef/takeUntilDestroyed
}
/** Retry loading data - clears error state */
retryLoad(): void {
this.loadLogs();
}
}