1-SEC contributors · Reviewed · Module: identity_monitor
Service Account Misuse: Activity Volume and Baseline Limits
Identity Fabric Monitor groups reported service-account activity by account ID.
What 1-SEC checks
Identity Fabric Monitor groups reported service-account activity by account ID. The first observation establishes local action and IP state. Activity volume is counted in an hourly window and a count above one thousand produces a high-volume finding. The helper also contains new-action and new-IP checks guarded by a baseline-age condition, and the handler can emit their corresponding findings when that condition is satisfied.
Required telemetry and context
Relevant input events: service_account_activity, api_key_usage. Use a trusted producer at the real application boundary.
account_idactionresourcesource_ip (event envelope)
Field names above describe relevant context, not a complete API request. Consult the current event contract for envelope and detail requirements. Preserve booleans, numbers, and strings as the receiving handler expects them. Agent-provided text cannot independently verify identity or authorization.
Deployment, prevention, and response
Report activity from the service or identity system that authenticated the account and retain trustworthy principal, resource and source context outside the agent prompt. Use the account-volume signal alongside application authorization and a separate durable audit trail. Test ordinary low-volume activity and the high-volume fixture before selecting a response. Keep service-account grants and credential rotation in the actual IAM system; this monitor does not implement least-privilege credentials or a secret store.
Record the source event, affected agent/host, actual target, policy decision, and response result. Review enforcement configuration separately from detection. A recommendation, selected action, dry-run record, and completed action are different outcomes.
Reproduce the source fixtures
From a checkout of the linked 1-SEC source revision, run the selected fixtures with the repository's Go toolchain. These commands run tests; they do not execute a real attack against your systems.
go test ./internal/modules/identity -run '^(TestServiceAccountMonitor_FirstAction|TestServiceAccountMonitor_HighVolume|TestServiceAccountMonitor_UnusualAction|TestServiceAccountMonitor_UnusualIP|TestMonitor_HandleEvent_ServiceAccountActivity)$' -count=1The first-action control and seeded-volume fixture verify their specific assertions. New-action/new-IP fixtures manipulate internal timestamps; they do not prove natural baseline maturation. The handler fixture verifies a high-volume alert after seeded state, not a real cloud IAM incident.
TestServiceAccountMonitor_FirstAction
Full fixture and assertions — run in its original package with its helpers.
func TestServiceAccountMonitor_FirstAction(t *testing.T) {
sm := NewServiceAccountMonitor()
anomaly := sm.RecordAndAnalyze("svc-1", "read", "/api/data", "10.0.0.1")
if anomaly.UnusualAction {
t.Error("first action should not trigger UnusualAction")
}
if anomaly.UnusualIP {
t.Error("first action should not trigger UnusualIP")
}
if anomaly.HighVolume {
t.Error("first action should not trigger HighVolume")
}
}TestServiceAccountMonitor_HighVolume
Full fixture and assertions — run in its original package with its helpers.
func TestServiceAccountMonitor_HighVolume(t *testing.T) {
sm := NewServiceAccountMonitor()
sm.RecordAndAnalyze("svc-4", "read", "/api/data", "10.0.0.1")
// Directly set action count to simulate high volume
sm.mu.Lock()
profile := sm.accounts["svc-4"]
profile.ActionCount = 1000
profile.CountWindow = time.Now() // within the hour window
sm.mu.Unlock()
anomaly := sm.RecordAndAnalyze("svc-4", "read", "/api/data", "10.0.0.1")
if !anomaly.HighVolume {
t.Errorf("expected HighVolume=true when ActionCount > 1000, got ActionCount=%d", anomaly.ActionCount)
}
}TestServiceAccountMonitor_UnusualAction
Full fixture and assertions — run in its original package with its helpers.
func TestServiceAccountMonitor_UnusualAction(t *testing.T) {
sm := NewServiceAccountMonitor()
sm.RecordAndAnalyze("svc-2", "read", "/api/data", "10.0.0.1")
// Simulate baseline period by manipulating internal state
sm.mu.Lock()
profile := sm.accounts["svc-2"]
profile.CountWindow = time.Now().Add(-48 * time.Hour)
profile.LastSeen = time.Now()
sm.mu.Unlock()
anomaly := sm.RecordAndAnalyze("svc-2", "delete_all", "/api/data", "10.0.0.1")
if !anomaly.UnusualAction {
t.Error("expected UnusualAction=true for new action after baseline period")
}
}TestServiceAccountMonitor_UnusualIP
Full fixture and assertions — run in its original package with its helpers.
func TestServiceAccountMonitor_UnusualIP(t *testing.T) {
sm := NewServiceAccountMonitor()
sm.RecordAndAnalyze("svc-3", "read", "/api/data", "10.0.0.1")
// Simulate baseline period
sm.mu.Lock()
profile := sm.accounts["svc-3"]
profile.CountWindow = time.Now().Add(-48 * time.Hour)
profile.LastSeen = time.Now()
sm.mu.Unlock()
anomaly := sm.RecordAndAnalyze("svc-3", "read", "/api/data", "192.168.1.100")
if !anomaly.UnusualIP {
t.Error("expected UnusualIP=true for new IP after baseline period")
}
if len(anomaly.KnownIPs) == 0 {
t.Error("expected KnownIPs to contain the original IP")
}
}TestMonitor_HandleEvent_ServiceAccountActivity
Full fixture and assertions — run in its original package with its helpers.
func TestMonitor_HandleEvent_ServiceAccountActivity(t *testing.T) {
cp := newCapPipeline()
m := startedMonitor(t, cp.pipeline)
// First action — establishes baseline
ev1 := core.NewSecurityEvent("test", "service_account_activity", core.SeverityInfo, "svc activity")
ev1.Details["account_id"] = "svc-test"
ev1.Details["action"] = "read"
ev1.Details["resource"] = "/api/data"
ev1.SourceIP = "10.0.0.1"
m.HandleEvent(ev1)
// Simulate baseline period
m.svcAcctMon.mu.Lock()
profile := m.svcAcctMon.accounts["svc-test"]
profile.CountWindow = time.Now().Add(-48 * time.Hour)
profile.LastSeen = time.Now()
m.svcAcctMon.mu.Unlock()
// Unusual action from new IP
ev2 := core.NewSecurityEvent("test", "service_account_activity", core.SeverityInfo, "svc activity")
ev2.Details["account_id"] = "svc-test"
ev2.Details["action"] = "delete_everything"
ev2.Details["resource"] = "/api/data"
ev2.SourceIP = "192.168.99.99"
m.HandleEvent(ev2)
if cp.count() == 0 {
t.Error("expected alert for unusual service account activity")
}
}Tuning, evaluation, and coverage limits
The anomaly fixtures manually seed an old CountWindow and recent LastSeen. Under ordinary sustained activity, hourly CountWindow resets prevent those fields from establishing the assumed twenty-four-hour baseline age; do not rely on natural new-action or new-IP maturation from those fixtures. High-volume monitoring is not proof of service-account compromise or enforcement of IAM permissions. Missing account IDs are ignored, state is local, and the module does not discover all cloud accounts or independently verify who used a credential.
Evaluate this behavior with representative authorized activity and a controlled known-positive case. Investigate the exact rule, inputs, and state before suppressing an alert. Do not assume a hard-coded heuristic is a configurable setting. Measure your own deployment; the source fixtures do not establish a benchmark, detection rate, compliance certification, or incident-prevention claim.
Related implemented controls
- Agent Output Secrets: API-Key Patterns, JWT Shapes and Leak Limits
- Agent Delegation Records: Verified Claims, Expiry and Scope Limits
- Connected Accounts: Link, Delegated Usage and Revocation Signals
- Agent Token Exchange: Audit Counters, Failure Signals and Scope Changes
- Agent Token Reuse: Revocation Signals and Observed Token Activity
- Agent OAuth Consent: Scope Accumulation and Grant-Rate Signals