feat(prospecting): implement security audit pipeline (Workstream 2A)
Complete security audit integration into the enrichment pipeline:
Backend:
- SecurityAuditService with 7 passive checks: HTTPS, SSL cert, security
headers, exposed files, cookies, server info, technology detection
- Constants file with SECURITY_HEADERS, EXPOSED_PATHS, SEVERITY_SCORES
- SecurityAuditResponse schema with JSON field validators + aliases
- Endpoints: POST /security-audit/{id}, POST /security-audit/batch
- Added to full_enrichment pipeline (Step 5, before scoring)
- get_pending_security_audit() query in prospect_service
Frontend:
- Security tab on prospect detail page with grade badge (A+ to F),
score/100, severity counts, HTTPS/SSL status, missing headers,
exposed files, technologies, and full findings list
- "Run Security Audit" button with loading state
- "Security Audit" batch button on scan-jobs page
Tested on batirenovation-strasbourg.fr: Grade D (50/100), 11 issues
found (missing headers, exposed wp-login, server version disclosure).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,10 +13,12 @@ function prospectDetail(prospectId) {
|
||||
campaignSends: [],
|
||||
loading: true,
|
||||
error: null,
|
||||
auditRunning: false,
|
||||
|
||||
activeTab: 'overview',
|
||||
tabs: [
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'security', label: 'Security' },
|
||||
{ id: 'interactions', label: 'Interactions' },
|
||||
{ id: 'campaigns', label: 'Campaigns' },
|
||||
],
|
||||
@@ -115,6 +117,39 @@ function prospectDetail(prospectId) {
|
||||
}
|
||||
},
|
||||
|
||||
async runSecurityAudit() {
|
||||
this.auditRunning = true;
|
||||
try {
|
||||
await apiClient.post('/admin/prospecting/enrichment/security-audit/' + this.prospectId);
|
||||
Utils.showToast('Security audit complete', 'success');
|
||||
await this.loadProspect();
|
||||
} catch (err) {
|
||||
Utils.showToast('Audit failed: ' + err.message, 'error');
|
||||
} finally {
|
||||
this.auditRunning = false;
|
||||
}
|
||||
},
|
||||
|
||||
gradeColor(grade) {
|
||||
if (!grade) return 'text-gray-400';
|
||||
if (grade === 'A+' || grade === 'A') return 'text-green-600 dark:text-green-400';
|
||||
if (grade === 'B') return 'text-blue-600 dark:text-blue-400';
|
||||
if (grade === 'C') return 'text-yellow-600 dark:text-yellow-400';
|
||||
if (grade === 'D') return 'text-orange-600 dark:text-orange-400';
|
||||
return 'text-red-600 dark:text-red-400';
|
||||
},
|
||||
|
||||
severityBadge(severity) {
|
||||
var classes = {
|
||||
critical: 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300',
|
||||
high: 'bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300',
|
||||
medium: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300',
|
||||
low: 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300',
|
||||
info: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',
|
||||
};
|
||||
return classes[severity] || classes.info;
|
||||
},
|
||||
|
||||
scoreColor(score) {
|
||||
if (score == null) return 'text-gray-400';
|
||||
if (score >= 70) return 'text-red-600';
|
||||
|
||||
@@ -53,6 +53,7 @@ function scanJobs() {
|
||||
'tech_scan': 'tech-scan',
|
||||
'performance_scan': 'performance',
|
||||
'contact_scrape': 'contacts',
|
||||
'security_audit': 'security-audit',
|
||||
'score_compute': 'score-compute',
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user