See what your FortiGate really allows.

RuleScope reads a FortiGate config backup, or the firewall itself through a read-only API token, and shows what is reachable from the internet, which rules are dead or redundant, where the device is not hardened, and what changed since the last run. It runs on your own machine with nothing but Python.

FGT-HQ, a fictional FGT60F 15 rules as RuleScope reads them. Score 46 of 100, 5 high-severity findings. Open the full report
IDRuleFrom, to Source, destinationService ActionWhat RuleScope found
10LAN-internetinternal to virtual-wan-linkall to allALLaccept
MediumInternet-bound accept rule without security profiles
MediumUnrestricted outbound internet access
11LAN-webinternal to wan1LAN_HALVES to allWEB_PORTSaccept
LowInternet-bound rule missing security profiles
LowRedundant rule
12block-bad-hostLAN_ZONE to wan2LAN_NET to Bad_HostALLdeny
HighDeny rule never applies
20web-to-filesdmz to internalWeb_Server to File_ServerSMBaccept
MediumAccept rule does not log traffic
21dmz-deny-landmz to internalall to allALLdeny
Nothing
22web-to-dbdmz to internalWeb_Server to DB_ServerMYSQLaccept
MediumAccept rule can never match
30inbound-webwan1 to dmzIsrael to VIP_WEBHTTPSaccept
Nothing
31inbound-all-to-dmzwan1 to dmzall to allHTTPSaccept
MediumInternet-facing rule without IPS
32rdp-to-fileserverwan1 to internalall to VIP_RDPRDPaccept
HighRisky service reachable from the internet
MediumInternet-facing rule without IPS
33dmz-host-static-natwan1 to dmzIsrael to VIP_ALLALLaccept
MediumInbound rule exposes all ports
LowInternet-facing rule without IPS
40m365internal to wan1LAN_NET to internet serviceinternet serviceaccept
MediumInternet-bound accept rule without security profiles
LowRedundant rule
41(no name)internal to wan1LAN_NET to Update_SiteHTTPSaccept (disabled)
InfoDisabled rule
InfoRule has no name or comment
50lan-ping-dmzinternal to dmzLAN_NET to allALL_ICMP, WEB_RANGEaccept
Nothing
51lan-https-dmzinternal to dmzFile_Server to Web_ServerPING, HTTPSaccept
LowRedundant rule
60promo-accessinternal to wan1LAN_NET to Update_SiteHTTPSaccept
MediumInternet-bound accept rule without security profiles
LowRule uses an expired schedule
LowRedundant rule

Open the sample output

Everything linked here is real RuleScope 0.4.2 output, rebuilt every night from the current code. The three firewalls are fictional, so nothing on this site describes a real network.

  • Fleet dashboard

    Three firewalls on one page: score, internet exposure, firmware support status and what changed since the previous run.

  • Audit report for FGT-HQ

    The report you hand to a client or keep for an audit: summary, priority actions, quick wins, exposure map, the 37-practice baseline with evidence, and a compliance appendix. Light and dark, five palettes, prints cleanly.

  • Change report for FGT-BR-SOUTH

    Two runs, and between them someone changed the firewall. RuleScope raised these alerts:

    • Management access added on internet-facing interface wan2: ssh
    • New admin account: contractor
  • Remediation script

    FortiOS commands for the findings. Anything that changes traffic, VPN peers or management access is commented out until you have reviewed it.

  • Findings as CSV

    One row per finding with severity, effort and compliance references, for Excel or a ticket system. The baseline is a CSV too, and everything is available as JSON.

What it checks

Internet exposure
Every service reachable from outside: port forwards, static NAT, routed inbound rules, admin GUI or SSH on WAN, the SSL-VPN portal. Risky services such as RDP, SMB and databases are called out, and severity depends on who can reach them.
Rule base
Deny rules that never apply, accept rules that can never match, redundant rules with the covering rule named, any-to-any, unrestricted outbound, zero-hit and stale rules, expired schedules.
Device hardening
Telnet and HTTP management, trusted hosts, admins without MFA, the default admin account, lockout and password policy, TLS versions, strong-crypto, SNMP, NTP, HA.
Firmware
FortiOS end-of-support dates, and the CVEs that affect the exact version from the NIST NVD, flagged when CISA lists them as exploited.
Logging and inspection
Accept rules without logging, no central logging, internet-bound rules without IPS, antivirus, DNS filter or application control, expired subscriptions.
VPN
Weak IPsec proposals and DH groups, aggressive mode with a pre-shared key, phase 2 without PFS, SSL-VPN on old TLS or the factory certificate.
Objects
Addresses, groups, services and virtual IPs that nothing references.

On top of the checks, a baseline of 37 practices from Fortinet's hardening guide and the CIS FortiGate benchmark is marked pass, partial, fail or not assessed, with the evidence from your configuration. A setting RuleScope could not read never counts as a pass.

Run it on your own firewall

You need Python 3.9 or later and nothing else.

  1. Get RuleScope

    git clone https://github.com/mrpash89/rulescope-fortigate.git
    cd rulescope-fortigate
  2. Audit a config backup

    Works offline, on a backup you downloaded or one a client sent you.

    python3 -m rulescope audit --config backup.conf --html report.html --cli fix.txt --csv findings.csv
  3. Or read the firewall directly

    With a read-only API token you also get hit counts, licenses and the change log. The certificate fingerprint is checked before the token is sent.

    python3 -m rulescope cert --host 192.0.2.1          # prints the SHA-256 fingerprint
    python3 -m rulescope audit --host 192.0.2.1 --token-file ~/.rulescope-token \
        --pin-sha256 FINGERPRINT --html report.html

The README covers the read-only API user, fleet mode, scheduled change tracking and alerts.

Built to run next to production

It never changes a firewall

RuleScope only reads. Fixes come as a script that you review and paste yourself.

Your configuration stays with you

No cloud service, no account, no telemetry. The only optional outbound request is the CVE lookup at the NIST NVD, and it sends a FortiOS version number.

Nothing to install

Python standard library only, with no third-party packages to vet or update.

Configs are treated as hostile

A backup can come from anyone. Names and comments in it cannot become script commands, spreadsheet formulas or markup, and randomized tests check that on every change.

Open source

Apache-2.0. Read the code, run the tests, send a pull request.