Security Analytics - v1.0.0
Anomaly Detection
AnomalyDetectionService orchestrates 8 pluggable detectors against the security_events stream. Each detector implements DetectorInterface — replace or extend any of them via the service provider.
The shipped detectors
| Detector | What it catches |
|---|---|
BruteForceDetector |
High volume of failed attempts against a single account within a window |
CredentialStuffingDetector |
High volume of failed attempts across many accounts from a single IP / ASN |
GeoVelocityDetector |
Logins from geographically distant locations in implausibly short time (impossible travel) |
PrivilegeEscalationDetector |
Unusual elevation patterns — granting roles, accessing admin endpoints from non-admin users |
AccessPatternDetector |
Sudden shift in URL access patterns vs the user's baseline |
BehavioralDetector |
Off-hours activity, unusual session length, device fingerprint changes |
StatisticalDetector |
Per-user metric values exceeding N standard deviations from the user's baseline |
RuleBasedDetector |
Custom rule definitions stored in config or DB — fully declarative |
Each detector is independently configurable via config('artisanpack.security-analytics.anomaly_detection.detectors.{slug}').
Running detection
The detection service runs automatically on a schedule:
php artisan security:detect-suspicious
Schedule this command every 5–10 minutes. It batches recent events through every enabled detector and writes Anomaly rows for matches.
To run synchronously inline (e.g. from a controller, against a specific event):
security_analytics()->detection()->analyze( $securityEvent );
Returns a Collection<Anomaly> of matches. Each Anomaly row carries the detector's name, severity, confidence, and detection metadata.
Baselines
Three detectors (StatisticalDetector, BehavioralDetector, AccessPatternDetector) rely on per-user baselines stored in UserBehaviorProfile. Update them on a schedule:
php artisan security:update-baselines
php artisan security:update-baselines --days=30 # baseline window size
Daily is appropriate for most apps. Baselines settle in ~7–14 days for typical users.
Reacting to detections
The AnomalyDetected event fires for every new Anomaly row. Subscribe to alert, escalate, or trigger an incident response playbook:
use ArtisanPackUI\SecurityAnalytics\Events\AnomalyDetected;
Event::listen( AnomalyDetected::class, function ( AnomalyDetected $event ): void {
if ( $event->anomaly->severity === 'high' ) {
security_analytics()->alerts()->sendForAnomaly( $event->anomaly );
}
} );
For automatic playbook-driven response see Incident response.
Tuning sensitivity
Each detector has its own knobs. For example, BruteForce:
'brute_force' => [
'enabled' => true,
'threshold' => 5, // failures to trigger
'window_minutes' => 15, // observation window
'lockout_count' => 3, // how many lockouts before escalation
],
Start with the defaults and tune based on false-positive rate. Track Anomaly rows by detector + severity to see which detectors are noisy in your environment.
Building a custom detector
See Custom detectors.