| name | fortianalyzer-ops |
| description | FortiAnalyzer log operations — policy-filtered traffic log query within a bounded time window, offset pagination, per-policy activity checks, and logging-device inventory. Use when asking whether traffic actually matched a firewall rule, investigating what hit a policy, or determining whether a rule is genuinely unused. |
| version | 1.0.0 |
| license | Apache-2.0 |
| tags | ["fortinet","fortianalyzer","logs","traffic","siem","firewall","audit","security"] |
| user-invocable | true |
| metadata | {"openclaw":{"requires":{"bins":"[Truncated]","env":"[Truncated]"}}} |
FortiAnalyzer Operations — the analyzer plane
MCP Server
- Server:
fortinet-mcp (NetClaw-authored, spec 080 / roadmap R3)
- Command:
$FORTINET_MCP_CMD
- Transport: stdio · JSON-RPC over
/jsonrpc (the same dialect FortiManager speaks)
- Requires:
FORTIANALYZER_HOST, FORTIANALYZER_API_TOKEN (FortiAnalyzer 7.2.2+ for token auth)
- Mode: read-only
The one rule that matters most here
"No logs matched" is NOT "this rule is unused."
This skill exists to answer "is this rule dead?" — and that question is dangerously
easy to answer wrongly. An empty result can mean:
- nothing matched in the window you queried (retention is finite; history is not)
- the device never forwarded logs to this analyzer at all
- logging is disabled on the rule itself
Reporting any of those as "unused" would license someone to delete a live firewall
rule. So an empty result returns the explicit outcome no_logs_in_window, never
ok and never an error, with a message saying what it does and does not prove.
This is the same error class as spec 078's "no advisories ≠ not vulnerable" and
spec 079's "no probes found ≠ outage", and it gets the same treatment: a separate,
named outcome that cannot be silently collapsed.
Where this plane sits
| Question | Plane | Skill |
|---|
| "Has anything actually matched this rule?" | analyzer | this skill |
| "What policy is intended?" | manager | fortimanager-ops |
| "What is the box running right now?" | device | fortigate-ops |
The manager knows a rule exists. Only the analyzer knows whether anyone ever
matched it. A configured rule is not a used rule.
Tools (4, all read-only)
| Tool | What it answers |
|---|
faz_query_logs | Traffic logs matching a filter within a bounded window |
faz_fetch_more | Next page, re-run at an offset |
faz_policy_activity | Did anything match policy N in this window? |
faz_list_devices | Which devices forward logs here — check this first |
Time windows are mandatory and always stated
If you supply no window, the tools apply the last 24 hours and say so in
scope.window_start / scope.window_end and in notes. An unbounded log query
against a busy analyzer is slow, expensive, and produces a result nobody can
interpret because they do not know what period it covers.
Every response echoes the window actually queried, not the one requested.
Pagination
faz_fetch_more re-runs the search at a new offset. It does not reuse
FortiAnalyzer's search task id (tid), because those are single-use and expire —
treating one as a durable cursor produces silent truncation, where you believe you
have all the results and you have some of them.
Workflow: is this firewall rule dead?
faz_list_devices — confirm the device that owns the rule actually forwards
logs here. If it does not, stop: an empty result would mean nothing.
faz_policy_activity with the policy id and an explicit, generous window.
- Read the outcome:
ok with sessions_matched > 0 → the rule is live. Do not remove it.
no_logs_in_window → nothing matched in that window. Not proof of disuse.
- Before concluding a rule is unused, verify: log forwarding on, retention covers
the period, logging enabled on the rule itself.
- Only then treat it as a removal candidate — and route the actual change through
fortimanager-ops, which enforces the two write gates.
Workflow: what hit this policy?
faz_query_logs with filter_expr (e.g. policyid=12) and a bounded window.
- Page with
faz_fetch_more using the returned next_offset while has_more.
- Correlate sources and destinations against
fmg_resolve_object to check whether
the traffic matches what the rule intended to permit.
Every response carries its plane and scope
{ "plane": "analyzer",
"scope": {"adom": "root", "window_start": "...", "window_end": "..."},
"outcome": "no_logs_in_window", "message": "... This is NOT evidence the rule is unused." }
The window is part of the scope, not an optional detail. A log result without its
window is uninterpretable, so a response that cannot state one is an error.
Integration with other skills
| Skill | How they compose |
|---|
fortimanager-ops | Find the rule (intent), then come here for whether it was used |
fortigate-ops | Device state; use together to separate "not configured" from "not used" |
fwrule-analyzer | Shadowed rules found there + zero activity here = strong removal case |
servicenow-change-workflow | Rule removal is a production change — CR required |
gait-session-tracking | Every query here is GAIT-audited automatically |
Important rules
- Never report an empty window as "unused".
- Always state the window you queried, including the default.
- Check
faz_list_devices before trusting silence.
- Read-only — findings here justify a change; they never make one.