// ARTEX's fixed user agents in web logs (The Tokyo Nomad, 4 Nov 2026). Read-only; last 24 hours. // Not executed in a live tenant; checked with Microsoft's Kusto parser against Microsoft Learn schemas. // Strings from the public artex-global source at v0.3.15-global.1 (d6964594) and its norma v0.4.3 SDK: // norma/0.4 = agent WebFetch; artex-enrich/1.0 = asset title probe; artex-selfupdate = updater (GitHub only). // A match is a lead: a fork can change these strings, and your own approved testing will match too. // Most ARTEX tools are curl, python or scanners with their own user agents; absence proves nothing. let Window = 24h; let ArtexAgents = @"(?i)^(?:norma/0\.|artex-enrich/|artex-selfupdate)"; union isfuzzy=true (W3CIISLog | where TimeGenerated > ago(Window) | project TimeGenerated, Source="W3CIISLog", ClientIP=cIP, Host=csHost, Path=csUriStem, Status=tostring(scStatus), UserAgent=csUserAgent), (AppServiceHTTPLogs | where TimeGenerated > ago(Window) | project TimeGenerated, Source="AppServiceHTTPLogs", ClientIP=CIp, Host=CsHost, Path=CsUriStem, Status=tostring(ScStatus), UserAgent), (AGWAccessLogs | where TimeGenerated > ago(Window) | project TimeGenerated, Source="AGWAccessLogs", ClientIP=ClientIp, Host, Path=RequestUri, Status=tostring(HttpStatus), UserAgent), (CommonSecurityLog | where TimeGenerated > ago(Window) | project TimeGenerated, Source="CommonSecurityLog", ClientIP=SourceIP, Host=DestinationHostName, Path=RequestURL, Status="", UserAgent=RequestClientApplication) | where UserAgent matches regex ArtexAgents | summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Requests=count(), Statuses=make_set(Status, 10), Paths=make_set(Path, 10) by Source, ClientIP, Host, UserAgent | order by LastSeen desc | take 100