Skip to main content

netops-auditor

Configuration audit for network devices. The auditor collects the configuration from the device itself, parses it with a platform-specific parser, evaluates the parsed configuration against a catalogue of deterministic rules, and reports findings - never the configuration itself, and never a credential.

This source tree targets netops-auditor/v0.2.7 (2026-09-24), which pins netops-core==0.2.4 and is published on PyPI (pip install netops-auditor); every version keeps its own release page and tag, and every release stays in the history of main as its own signed commit - see docs/releasing.md. The component is usable from the CLI, and its shape is fixed by its gates rather than by its documentation.

What it does

  • Rules as data. One catalogue per platform, as JSON under src/netops_auditor/catalog/: identifier, version, check, class (fakt or usudek), severity, evidence fields, remediation, and optional compliance references. A rule without a positive and a negative fixture does not enter the catalogue - the gate rejects it.
  • Two audited platforms. FortiOS carries 18 rules over the tree of config / edit / set: VDOM scope, dangling policy references, WAN administrative access, UTM inspection without an SSL profile, a missing syslog target, no NTP sync, and twelve hardening rules taken from the CIS FortiGate 7.4.x Benchmark v1.0.1 and the hardening chapter of the FortiOS 8.0.0 Best Practices - see docs/cis-mapping.md. ExtremeXOS and Switch Engine carry 4 rules over the flat command list of show configuration: no SNTP or NTP sync, a missing syslog target, a default SNMP community, and Telnet left enabled. The platform picks the parser and the catalogue; there is no shared model between them and none is planned until a rule needs one.
  • Collects its own configuration through a pinned channel. The inventory selects file, fortios-rest, or ssh; there is no fallback ladder. SSH supports FortiOS and EXOS, REST is FortiOS-only, and file reads a dump collected elsewhere. Live measurements through 20 September 2026 cover FortiOS SSH, EXOS SSH collection followed by all four rules, and FortiOS REST collection followed by all six rules. The EXOS measurement includes local mutations producing the expected findings; the REST measurement includes a deliberately absent required section producing an incomplete-snapshot finding. These are dated measurements, not coverage of every firmware and access profile. See docs/channels.md, docs/inventory.md and the family's verified platform support.
  • Reaches a device through netops-core, and does not vendor it. The inventory (file version 2), the credential store (file version 2), the host key trust and the SSH transport are the shared access layer of the family, pinned as netops-core==0.2.4. Unlike netops-helper, whose release archive vendors a copy of netops-core inside itself, the auditor carries none: pip install netops-auditor takes the core from PyPI, and from the release archives the operator installs the netops-core source archive of exactly that version beside it (see docs/releasing.md). What stays here is the policy - the auditor section of an entry, which kind of credential a channel takes, the step table of a platform and the audit itself.
  • Never changes a device - though the account behind it is not read-only either. Not a policy, not an interface, not even a console setting: with an active FortiOS pager the collection refuses instead of disabling it. Reaching the device at all still needs an elevated account: a super_admin administrator on FortiOS - a profile that could write to the device, chosen because a weaker one silently returns an incomplete configuration instead of an error - and an administrator account on ExtremeXOS, because a user-level account there is refused show configuration outright. What keeps the collector itself from writing is the fixed step table and the absence of any other command on the channel, not the account; see docs/channels.md.
  • Keeps secrets out of findings. A finding carries an object reference and line numbers, never the configuration text. A canary fixture per platform - twenty marked secrets on FortiOS, eleven on EXOS - must not leak a single one into a finding, an MCP answer, or a CLI report.
  • Knows what changed, and never across tenants. Baseline in the database, suppressions with a mandatory expiry in a reviewed JSON file, four evaluated finding states, an explicit not-evaluated state for findings carried across an incomplete audit, and a freshness threshold - every command that touches them takes a required --tenant. The tenant is part of a finding's identity: the fingerprint is the sha256 of rule id, rule version, tenant, device name and object key, computed by one shared function, so the same device audited for two tenants produces two different fingerprints and one tenant's waiver cannot reach the other. A suppression file therefore names its tenant as a required field at file version 2, and the CLI and the read-only MCP surface refuse a file bound elsewhere through the same function rather than one warning where the other is silent. The store carries a schema version for the same reason. Both older shapes are refused fail-closed with the migration command in the message, and both migrations - migrate-suppressions and migrate-store - are explicit steps that never run on the side. The credential store, the suppression schema, the two migrations, the byte budget of the REST channel, the exit codes and the two refusals that end a run before the catalogue is reached are in docs/configuration.md.
  • Read-only MCP surface - a different and smaller boundary than the collector's. Six tools (audit_status, list_rules, rule_detail, list_findings, finding_detail, compare) over a SQLite store the server opens read-only for one configured tenant. It imports neither the collection code, nor the credential store, nor the inventory, so it cannot reach a device and needs none of the collector's privilege: the collector needs a super_admin or administrator account on the device itself, the server needs only local filesystem read access to a store somebody else already wrote.
  • The gate reads what is released. scripts/check_gates.py scans the whole released file set, not only the fixtures, and it runs inside an exported archive where no repository is left. What may appear in that set is an allowlist: RFC 5737 and loopback addresses, 2001:db8::/32, RFC 7042 MAC addresses, RFC 2606 names, example.invalid, and the vendor domains of the documentation. Anything else stops the release. Outside a repository - which is what an exported archive is - the gate also holds the file set itself: every file present must be in the release selection and in release-manifest.json, and every file those two name must be present, so neither an added nor a removed file survives it.

Management rules and operator policy

The catalogue also carries seven FortiOS and five EXOS management checks, released in 0.2.5. These cover the Admin change scope and accept an optional operator policy. See management policy for scope, configuration, coverage reporting and limitations.

Running it

The auditor needs netops-core and nothing else from outside the standard library; pip install netops-auditor installs both from PyPI, and in this repository the core is taken from the tree. fastmcp is needed by the MCP surface alone and is pinned in requirements-mcp.txt; the CLI runs without it, and python -m netops_auditor.mcp_server started without fastmcp says so in one line instead of raising an import error. Installing the distribution puts the command line on the path as netops-auditor, with the rule catalogues inside the package.

PYTHONPATH=src:../netops-core/src python3 -m netops_auditor --help
python3 -m pytest -q                         # the test suite
python3 scripts/check_gates.py               # the gate of a release

Three subcommands, each taking --tenant: run evaluates a configuration file already on disk, collect reaches a device through the inventory and evaluates what comes back, and status reports the freshness of the last audit. merge-sarif combines SARIF reports of run --sarif; the two migrations are described in docs/configuration.md.

SARIF and GitHub code scanning

run --sarif writes the report as one SARIF 2.1.0 run instead of text or JSON (--json and --sarif exclude each other). The run describes every rule of the catalogue, and each finding becomes a result with the configuration file and line, the object key as a logical location, the finding fingerprint under partialFingerprints (netopsFingerprint/v2) and a suppressed finding marked as an accepted external suppression. Severity maps to the SARIF level (high to error, medium to warning, low and info to note) and, for GitHub, to security-severity 8.0, 5.0 and 3.0. The evidence fields travel as result properties exactly as in the JSON report, never the configuration text. merge-sarif --output FILE INPUT... combines several such files of the same auditor version into one run, so that one upload to code scanning carries all of them.

netops-auditor run --platform fortios --tenant example --device fw-a --config backups/fw-a.conf --sarif > fw-a.sarif
netops-auditor merge-sarif --output all.sarif fw-a.sarif fw-b.sarif

The repository ships the same path as a composite GitHub Action in action.yml. It audits configuration files already committed to the repository - no device, credential or network access - and writes one SARIF file. It sets up Python 3.13 without changing the environment of later steps and runs the auditor and netops-core from the sources at the referenced commit, so pin it to the full commit SHA of an auditor release. Every file becomes a device named by its path, so renaming a file changes the fingerprints of its findings. A pattern that matches nothing, an unreadable file or an unusable policy fails the step.

permissions:
  contents: read
  security-events: write
jobs:
  audit:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - id: audit
        uses: radek-cerny-soukr/netops/components/netops-auditor@<commit of an auditor release>
        with:
          platform: fortios
          configs: |
            backups/**/*.conf
      - uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
        with:
          sarif_file: ${{ steps.audit.outputs.sarif-file }}
          category: netops-auditor-fortios

Inputs: platform (fortios or exos, one catalogue per step), configs (one glob pattern per line), tenant-name (default default), policy-file (optional operator policy) and output-file (default netops-auditor.sarif). Outputs: sarif-file and results, the number of findings. The step does not fail on findings; code scanning decides what blocks a pull request. Uploading needs code scanning enabled for the repository.

Boundaries

The auditor is not a compliance product: rules may carry refs to the vendor command reference or to CIS, ZKB, or DORA, but no profile is built and no compliance is claimed. The CIS references name the recommendation a rule checks and, in known_false_positives, the part of it the rule does not check; docs/cis-mapping.md also lists the recommendations no rule covers. Rules exist for FortiOS and for EXOS, and the EXOS catalogue is four rules wide - time, logging, SNMP communities and Telnet - which is a beginning, not coverage; what each of them cannot see is in docs/channels.md. The dated live EXOS measurement and its reciprocal local mutations are recorded in the support matrix; they do not establish coverage beyond the four catalogue rules. A finding of class usudek is a judgement and is never emitted at high severity.

The auditor ships no image of its own either, so the ssh channel runs the OpenSSH client already installed on the host and inherits that client's own vulnerabilities, unfiltered by any isolating container. Keeping that client current is the operator's responsibility, not this component's.

MIT licensed. Part of the netops family.

Release files for netops-auditor 0.2.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for netops-auditor 0.2.7
File Size Uploaded
netops_auditor-0.2.7.tar.gz 135.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for netops-auditor 0.2.7
File Interpreter ABI Platform
netops_auditor-0.2.7-py3-none-any.whl Python 3 none any Details

Total release size: 197.0 kB

Release files / netops_auditor-0.2.7.tar.gz

Download URL netops_auditor-0.2.7.tar.gz
Size 135.5 kB
Tags Source
SHA-256 checksum
How to use checksums
78003e22c0d9dd3cbd9a661a683252c5a80cb915e0a6f46ef4b5b4103c1fb30f
BLAKE2b-256 checksum
How to use checksums
0cacc5d88aebd417c68e79271f6fd6799ad4d2c78b34f44f371bf1f72432c4d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / netops_auditor-0.2.7-py3-none-any.whl

Download URL netops_auditor-0.2.7-py3-none-any.whl
Size 61.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
deb00b6ee194a67bac278f811292ec8393d0cede5c99d915ddb052442a99291d
BLAKE2b-256 checksum
How to use checksums
3db9acc5fe6acaf56a441dade70cc744f613b31ce97f4102a6a947fca46e4d03
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.7 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page