nsmcp::AccessAnalytics method correlateWarnings (public)
<instance of nsmcp::AccessAnalytics> correlateWarnings samples \ redactor
Defined in /usr/local/ns/tcl/nsmcp/access-analytics.tcl
Correlate bounded warning examples with retained cache rows only. The existing start-time index bounds lookups; no raw log is scanned and no ingestion/backfill is triggered. Addresses are never selected.
- Parameters:
- samples (required)
- Up to three dictionaries with requestIdentifier and timestamp; optional userAgent and userAgentMayBeTruncated from system-log diagnostics take precedence over cached User-Agent.
- redactor (required)
- Redactor applied to returned text fields.
- Returns:
- Rooted JSON triples with sampled evidence and explicit missing/ambiguous counts.
- Testcases:
- No testcase defined.
Source code: if {[llength $samples] > 3} {error "Too many warning correlation samples"} set fields {}; set matched 0; set missing 0; set ambiguous 0; set i 0 set db [${:database} connect] try { foreach sample $samples { set request_id [dict get $sample requestIdentifier] set at [dict get $sample timestamp] if {![::nsmcp::logRecordParser validConnectionIdentifier $request_id] || ![string match "-conn:[ns_info server]:*" $request_id] || ![string is double -strict $at]} {incr missing; continue} set lower [expr {$at-60}]; set upper [expr {$at+1}] set rows [$db execute {SELECT request_id,start,path,pool,method,status,authenticationState,userAgent FROM nsmcp_access_requests WHERE start >= $lower AND start <= $upper AND request_id=$request_id LIMIT 2}] if {[llength $rows] == 0} {incr missing; continue} if {[llength $rows] > 1} {incr ambiguous; continue} set row [lindex $rows 0]; set item {} set uaSource access-cache set authSource access-cache if {[dict exists $sample authenticationState] && [dict get $sample authenticationState] in {anonymous authenticated}} { dict set row authenticationState [dict get $sample authenticationState] set authSource system-log } set uaTruncated [expr {[string length [dict get $row userAgent]] >= 1024}] if {[dict exists $sample userAgent]} { dict set row userAgent [dict get $sample userAgent] set uaSource system-log set uaTruncated [dict get $sample userAgentMayBeTruncated] } foreach key {path pool method authenticationState userAgent} { lappend item $key string [$redactor message [dict get $row $key]] } lappend item requestIdentifier string $request_id start number [dict get $row start] status number [dict get $row status] warningTimestamp number $at authenticationSource string $authSource userAgentAvailable boolean [expr {[dict get $row userAgent] ne ""}] userAgentSource string $uaSource userAgentMayBeTruncated boolean $uaTruncated lappend fields $i object $item; incr i; incr matched } } finally {$db close} return [list {} object [list available boolean true scope string sampled-warning-requests requested number [llength $samples] matched number $matched missing number $missing ambiguous number $ambiguous records array $fields limitations string {Bounded examples, not aggregate bot attribution. Exact connection identifier and start time within 60 seconds before to 1 second after the warning are required. Cache lag, eviction, long requests, legacy rows without User-Agent, and absent identifiers can prevent matches. No raw access-log scan or cache refresh was performed. Multiple candidates are not attributed. Pool and User-Agent alone do not prove bot or human identity. No peer addresses are returned.}]]XQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)