cidr-audit

Audit IPv4 CIDR allow/deny rules for overlap, shadowing, and risky network ranges in pure MoonBit

cidr
ipv4
network
audit
security
moon add BeiLaDuo/cidr-audit@0.1.1
Download zip
Author
Version
0.1.1
License
Apache-2.0
Last updated
18 days ago
Downloads
4
README

#MoonCIDR Audit

MoonCIDR Audit is a pure MoonBit library for auditing ordered IPv4/CIDR allow and deny rules. It helps gateway, reverse proxy, cloud security group, and operations configuration authors find rule shadowing, dangerous wide ranges, non-canonical CIDR input, duplicate rules, and risky special address spaces.

The project is intentionally small in scope: it does not try to be a firewall, packet filter, or network daemon. It focuses on the reusable analysis layer that other MoonBit tools can build on.

#Features

  • Parse IPv4 addresses and CIDR blocks without external dependencies.
  • Normalize CIDR blocks such as 192.168.1.99/24 to 192.168.1.0/24.
  • Evaluate ordered allow and deny rules.
  • Detect duplicate rules, shadowed rules, conflicting overlaps, redundant overlaps, global deny rules, and very wide allow rules.
  • Classify private, loopback, link-local, multicast, unspecified, and broadcast address ranges.
  • Provide strict, gateway, and quiet audit policies.
  • Produce plain text, Markdown table, and JSON-like reports.
  • Explain why a probe IP matches a rule.
  • Compare two rule sets and summarize risky changes.
  • Produce remediation hints for common findings.
  • Merge overlapping or adjacent address ranges and find coverage holes.
  • Deduplicate contained CIDRs and recursively aggregate sibling networks.
  • Import native rules, Nginx allow/deny directives, and CSV rule lists.
  • Apply configurable CI deployment gates to audit reports.

#Quick Start

Run the main demo:

moon run ./cmd/main

Run example scenarios:

moon run ./examples/gateway moon run ./examples/cloud

The main demo now walks through auditing, probe decisions, detailed explanations, a release gate, coverage holes, CIDR aggregation, and Nginx configuration import.

Run checks:

moon check moon build moon test

#Basic Usage

let ruleset = @cidr.parse_rules([
"allow 10.0.0.0/8 internal network",
"deny 10.1.2.0/24 blocked subnet",
"allow 192.168.1.99/24 host bits will be normalized",
"deny 0.0.0.0/0 default deny",
])

let report = ruleset.audit()
println(report.text_report())

let ip = @cidr.IPv4::parse("10.1.2.9").unwrap()
println(ruleset.decide(ip).summary())

#Example Findings

MoonCIDR Audit can report findings like:

[critical] too_wide_allow R1: allow rule covers a very wide network: 10.0.0.0/8 [critical] shadowed_rule R2 related=R1: rule is already covered by earlier allow rule R1 [info] non_canonical_cidr R3: input 192.168.1.99/24 was normalized to 192.168.1.0/24 [critical] global_deny R4: deny rule blocks the entire IPv4 space

#API Overview

Important types:

  • IPv4: parsed IPv4 address stored as UInt.
  • CidrBlock: normalized IPv4 CIDR block.
  • RuleAction: Allow or Deny.
  • Rule: ordered rule with id, action, block, source block, and note.
  • RuleSet: parsed rule collection with decision and audit helpers.
  • AuditPolicy: strict, gateway, and quiet policy presets.
  • AuditReport: findings, severity counts, risk score, and report rendering.
  • DiffReport: change report between two rule sets.
  • FixHint: remediation suggestion generated from an audit finding.
  • AddressRange: closed IPv4 interval used by coverage analysis.
  • AggregationReport: CIDR deduplication and sibling merge result.
  • ImportReport: structured native, Nginx, or CSV import result.
  • GatePolicy and GateResult: configurable CI release decision.

Common functions:

  • IPv4::parse
  • CidrBlock::parse
  • Rule::parse
  • parse_rules
  • RuleSet::audit
  • RuleSet::audit_with_policy
  • RuleSet::decide
  • RuleSet::explain_ip
  • RuleSet::diff_from
  • RuleSet::coverage_ranges
  • RuleSet::coverage_report
  • aggregate_cidr_strings
  • import_nginx_access
  • import_csv_rules
  • AuditReport::evaluate_gate
  • AuditReport::text_report
  • AuditReport::json_report
  • AuditReport::hint_report

#Project Boundary

MoonCIDR Audit currently focuses on IPv4 and ordered CIDR rules. It does not implement IPv6, kernel integration, packet capture, live network access, or cloud-provider credentials. Its importers are intentionally offline and limited to reviewable text formats. Provider-specific adapters can be added later without changing the core audit model.

#Hackathon Value

MoonBit already has examples and packages around networking, URLs, sockets, and general parsing. MoonCIDR Audit targets a different gap: auditing network rule sets before they are deployed. This is useful for configuration review, CI checks, teaching network rule behavior, and building safer MoonBit developer tools.

#Submission Materials

  • One-page application: docs/submission-application-one-page.md
  • Full application notes: docs/submission-application.md
  • PDF application copy: docs/submission-application.pdf
  • Novelty review: docs/novelty-review.md
  • Submission checklist: docs/submission-checklist.md

#License

Apache-2.0.

#
AddressRange

pub struct AddressRange {
first : IPv4
last : IPv4
source_count : Int
} derive(Eq,
Debug
)

A closed IPv4 address interval.

source_count records how many original intervals contributed to this range after union operations. It is useful when explaining why a range was produced, but does not change equality or containment semantics.

#
AddressRange::address_count_label

fn AddressRange::address_count_label(self : AddressRange) -> String

#
AddressRange::contains_ip

fn AddressRange::contains_ip(self : AddressRange, ip : IPv4) -> Bool

#
AddressRange::contains_range

fn AddressRange::contains_range(self : AddressRange, other : AddressRange) -> Bool

#
AddressRange::first

fn AddressRange::first(self : AddressRange) -> IPv4

#
AddressRange::from_block

fn AddressRange::from_block(block : CidrBlock) -> AddressRange

#
AddressRange::last

fn AddressRange::last(self : AddressRange) -> IPv4

#
AddressRange::merge

fn AddressRange::merge(self : AddressRange, other : AddressRange) -> Result[AddressRange, String]

#
AddressRange::new

fn AddressRange::new(first : IPv4, last : IPv4, source_count? : Int) -> Result[AddressRange, String]

#
AddressRange::overlaps

fn AddressRange::overlaps(self : AddressRange, other : AddressRange) -> Bool

#
AddressRange::source_count

fn AddressRange::source_count(self : AddressRange) -> Int

#
AddressRange::summary

fn AddressRange::summary(self : AddressRange) -> String

#
AddressRange::to_string

fn AddressRange::to_string(self : AddressRange) -> String

#
AddressRange::touches

fn AddressRange::touches(self : AddressRange, other : AddressRange) -> Bool

#
AggregationReport

pub struct AggregationReport {
input_count : Int
valid_count : Int
duplicate_count : Int
contained_count : Int
merged_pair_count : Int
blocks : Array[CidrBlock]
errors : Array[String]
} derive(
Debug
)

Result of normalizing, deduplicating, and aggregating CIDR blocks.

#
AggregationReport::blocks

#
AggregationReport::changed

fn AggregationReport::changed(self : AggregationReport) -> Bool

#
AggregationReport::contained_count

fn AggregationReport::contained_count(self : AggregationReport) -> Int

#
AggregationReport::duplicate_count

fn AggregationReport::duplicate_count(self : AggregationReport) -> Int

#
AggregationReport::errors

fn AggregationReport::errors(self : AggregationReport) -> Array[String]

#
AggregationReport::input_count

fn AggregationReport::input_count(self : AggregationReport) -> Int

#
AggregationReport::merged_pair_count

fn AggregationReport::merged_pair_count(self : AggregationReport) -> Int

#
AggregationReport::output_count

fn AggregationReport::output_count(self : AggregationReport) -> Int

#
AggregationReport::reduction_count

fn AggregationReport::reduction_count(self : AggregationReport) -> Int

#
AggregationReport::summary

fn AggregationReport::summary(self : AggregationReport) -> String

#
AggregationReport::text_report

fn AggregationReport::text_report(self : AggregationReport) -> String

#
AggregationReport::valid_count

fn AggregationReport::valid_count(self : AggregationReport) -> Int

#
AuditPolicy

pub struct AuditPolicy {
wide_allow_prefix : Int
flag_private : Bool
flag_special_ranges : Bool
flag_non_canonical : Bool
flag_redundant_overlap : Bool
} derive(Eq,
Debug
)

Controls how noisy or strict CIDR audits should be.

#
AuditPolicy::flag_non_canonical

fn AuditPolicy::flag_non_canonical(self : AuditPolicy) -> Bool

#
AuditPolicy::flag_private

fn AuditPolicy::flag_private(self : AuditPolicy) -> Bool

#
AuditPolicy::flag_redundant_overlap

fn AuditPolicy::flag_redundant_overlap(self : AuditPolicy) -> Bool

#
AuditPolicy::flag_special_ranges

fn AuditPolicy::flag_special_ranges(self : AuditPolicy) -> Bool

#
AuditPolicy::gateway

fn AuditPolicy::gateway() -> AuditPolicy

#
AuditPolicy::quiet

fn AuditPolicy::quiet() -> AuditPolicy

#
AuditPolicy::strict

fn AuditPolicy::strict() -> AuditPolicy

#
AuditPolicy::wide_allow_prefix

fn AuditPolicy::wide_allow_prefix(self : AuditPolicy) -> Int

#
AuditReport

pub struct AuditReport {
rules : Array[Rule]
findings : Array[Finding]
parse_errors : Array[String]
} derive(
Debug
)

#
AuditReport::count_hint_kind

fn AuditReport::count_hint_kind(self : AuditReport, kind : HintKind) -> Int

#
AuditReport::count_kind

fn AuditReport::count_kind(self : AuditReport, kind : FindingKind) -> Int

#
AuditReport::count_severity

fn AuditReport::count_severity(self : AuditReport, severity : Severity) -> Int

#
AuditReport::critical_count

fn AuditReport::critical_count(self : AuditReport) -> Int

#
AuditReport::evaluate_gate

fn AuditReport::evaluate_gate(self : AuditReport, policy : GatePolicy) -> GateResult

#
AuditReport::finding_count

fn AuditReport::finding_count(self : AuditReport) -> Int

#
AuditReport::findings

fn AuditReport::findings(self : AuditReport) -> Array[Finding]

#
AuditReport::fix_hints

fn AuditReport::fix_hints(self : AuditReport) -> Array[FixHint]

#
AuditReport::hint_report

fn AuditReport::hint_report(self : AuditReport) -> String

#
AuditReport::info_count

fn AuditReport::info_count(self : AuditReport) -> Int

#
AuditReport::json_report

fn AuditReport::json_report(self : AuditReport) -> String

Builds a stable JSON-like report without external dependencies.

#
AuditReport::parse_errors

fn AuditReport::parse_errors(self : AuditReport) -> Array[String]

#
AuditReport::recommended_action

fn AuditReport::recommended_action(self : AuditReport) -> String

#
AuditReport::risk_level

fn AuditReport::risk_level(self : AuditReport) -> String

#
AuditReport::risk_score

fn AuditReport::risk_score(self : AuditReport) -> Int

#
AuditReport::rules

fn AuditReport::rules(self : AuditReport) -> Array[Rule]

#
AuditReport::summary_table

fn AuditReport::summary_table(self : AuditReport) -> String

#
AuditReport::text_report

fn AuditReport::text_report(self : AuditReport) -> String

Builds a compact human-readable audit report.

#
AuditReport::warning_count

fn AuditReport::warning_count(self : AuditReport) -> Int

#
CidrBlock

pub struct CidrBlock {
network : IPv4
prefix : Int
} derive(Eq,
Debug
)

A normalized IPv4 CIDR block.

#
CidrBlock::broadcast

fn CidrBlock::broadcast(self : CidrBlock) -> IPv4

#
CidrBlock::contains_block

fn CidrBlock::contains_block(self : CidrBlock, other : CidrBlock) -> Bool

#
CidrBlock::contains_ip

fn CidrBlock::contains_ip(self : CidrBlock, ip : IPv4) -> Bool

#
CidrBlock::equal_range

fn CidrBlock::equal_range(self : CidrBlock, other : CidrBlock) -> Bool

#
CidrBlock::has_host_bits

fn CidrBlock::has_host_bits(input : String) -> Bool

#
CidrBlock::network

fn CidrBlock::network(self : CidrBlock) -> IPv4

#
CidrBlock::new

fn CidrBlock::new(address : IPv4, prefix : Int) -> Result[CidrBlock, String]

#
CidrBlock::new_unchecked

fn CidrBlock::new_unchecked(network : IPv4, prefix : Int) -> CidrBlock

#
CidrBlock::overlaps

fn CidrBlock::overlaps(self : CidrBlock, other : CidrBlock) -> Bool

#
CidrBlock::parse

fn CidrBlock::parse(input : String) -> Result[CidrBlock, String]

#
CidrBlock::parse_raw

fn CidrBlock::parse_raw(input : String) -> Result[(IPv4, Int), String]

#
CidrBlock::prefix

fn CidrBlock::prefix(self : CidrBlock) -> Int

#
CidrBlock::size_label

fn CidrBlock::size_label(self : CidrBlock) -> String

#
CidrBlock::to_string

fn CidrBlock::to_string(self : CidrBlock) -> String

#
CoverageStats

pub struct CoverageStats {
total_rules : Int
allow_rules : Int
deny_rules : Int
private_rules : Int
public_rules : Int
special_rules : Int
widest_prefix : Int
narrowest_prefix : Int
} derive(Eq,
Debug
)

Aggregate facts about a rule set.

#
CoverageStats::allow_rules

fn CoverageStats::allow_rules(self : CoverageStats) -> Int

#
CoverageStats::deny_rules

fn CoverageStats::deny_rules(self : CoverageStats) -> Int

#
CoverageStats::narrowest_prefix

fn CoverageStats::narrowest_prefix(self : CoverageStats) -> Int

#
CoverageStats::private_rules

fn CoverageStats::private_rules(self : CoverageStats) -> Int

#
CoverageStats::public_rules

fn CoverageStats::public_rules(self : CoverageStats) -> Int

#
CoverageStats::special_rules

fn CoverageStats::special_rules(self : CoverageStats) -> Int

#
CoverageStats::summary

fn CoverageStats::summary(self : CoverageStats) -> String

#
CoverageStats::total_rules

fn CoverageStats::total_rules(self : CoverageStats) -> Int

#
CoverageStats::widest_prefix

fn CoverageStats::widest_prefix(self : CoverageStats) -> Int

#
Decision

pub struct Decision {
matched : Bool
action : RuleAction
rule_id : String
block : String
} derive(Eq,
Debug
)

#
Decision::action

fn Decision::action(self : Decision) -> RuleAction

#
Decision::block

fn Decision::block(self : Decision) -> String

#
Decision::matched

fn Decision::matched(self : Decision) -> Bool

#
Decision::rule_id

fn Decision::rule_id(self : Decision) -> String

#
Decision::summary

fn Decision::summary(self : Decision) -> String

#
DiffReport

pub struct DiffReport {
changes : Array[RuleChange]
} derive(
Debug
)

#
DiffReport::change_count

fn DiffReport::change_count(self : DiffReport) -> Int

#
DiffReport::changes

fn DiffReport::changes(self : DiffReport) -> Array[RuleChange]

#
DiffReport::count_kind

fn DiffReport::count_kind(self : DiffReport, kind : RuleChangeKind) -> Int

#
DiffReport::is_empty

fn DiffReport::is_empty(self : DiffReport) -> Bool

#
DiffReport::risk_level

fn DiffReport::risk_level(self : DiffReport) -> String

#
DiffReport::risky_count

fn DiffReport::risky_count(self : DiffReport) -> Int

#
DiffReport::text_report

fn DiffReport::text_report(self : DiffReport) -> String

#
Finding

pub struct Finding {
kind : FindingKind
severity : Severity
rule_id : String
related_rule_id : String
message : String
} derive(Eq,
Debug
)

#
Finding::kind

fn Finding::kind(self : Finding) -> FindingKind

#
Finding::message

fn Finding::message(self : Finding) -> String

#
Finding::new

fn Finding::new(kind : FindingKind, severity : Severity, rule_id : String, message : String, related_rule_id? : String) -> Finding

#
Finding::related_rule_id

fn Finding::related_rule_id(self : Finding) -> String

#
Finding::rule_id

fn Finding::rule_id(self : Finding) -> String

#
Finding::severity

fn Finding::severity(self : Finding) -> Severity

#
FindingKind

pub(all) enum FindingKind {
DuplicateRule
ShadowedRule
ConflictingOverlap
RedundantOverlap
TooWideAllow
GlobalDeny
PrivateRange
LoopbackRange
LinkLocalRange
MulticastRange
NonCanonicalCidr
ParseError
} derive(Eq,
Debug
)

Classifies why a rule deserves attention.

#
FindingKind::label

fn FindingKind::label(self : FindingKind) -> String

#
FixHint

pub struct FixHint {
kind : HintKind
severity : Severity
rule_id : String
text : String
} derive(Eq,
Debug
)

#
FixHint::kind

fn FixHint::kind(self : FixHint) -> HintKind

#
FixHint::new

fn FixHint::new(kind : HintKind, severity : Severity, rule_id : String, text : String) -> FixHint

#
FixHint::rule_id

fn FixHint::rule_id(self : FixHint) -> String

#
FixHint::severity

fn FixHint::severity(self : FixHint) -> Severity

#
FixHint::summary

fn FixHint::summary(self : FixHint) -> String

#
FixHint::text

fn FixHint::text(self : FixHint) -> String

#
GatePolicy

pub struct GatePolicy {
name : String
max_critical : Int
max_warning : Int
max_risk_score : Int
allow_parse_errors : Bool
} derive(Eq,
Debug
)

Thresholds used to decide whether a rule set may pass a CI or release gate.

#
GatePolicy::allow_parse_errors

fn GatePolicy::allow_parse_errors(self : GatePolicy) -> Bool

#
GatePolicy::development

fn GatePolicy::development() -> GatePolicy

Development policy: blocks only very risky reports and parse failures.

#
GatePolicy::max_critical

fn GatePolicy::max_critical(self : GatePolicy) -> Int

#
GatePolicy::max_risk_score

fn GatePolicy::max_risk_score(self : GatePolicy) -> Int

#
GatePolicy::max_warning

fn GatePolicy::max_warning(self : GatePolicy) -> Int

#
GatePolicy::name

fn GatePolicy::name(self : GatePolicy) -> String

#
GatePolicy::new

fn GatePolicy::new(name : String, max_critical : Int, max_warning : Int, max_risk_score : Int, allow_parse_errors? : Bool) -> Result[GatePolicy, String]

#
GatePolicy::release

fn GatePolicy::release() -> GatePolicy

Strict release policy: no critical findings, warnings, or parse errors.

#
GatePolicy::review

fn GatePolicy::review() -> GatePolicy

Review policy: allows a small warning budget but no critical findings.

#
GatePolicy::summary

fn GatePolicy::summary(self : GatePolicy) -> String

#
GateReason

pub struct GateReason {
code : String
actual : Int
limit : Int
message : String
} derive(Eq,
Debug
)

#
GateReason::actual

fn GateReason::actual(self : GateReason) -> Int

#
GateReason::code

fn GateReason::code(self : GateReason) -> String

#
GateReason::limit

fn GateReason::limit(self : GateReason) -> Int

#
GateReason::message

fn GateReason::message(self : GateReason) -> String

#
GateReason::new

fn GateReason::new(code : String, actual : Int, limit : Int, message : String) -> GateReason

#
GateReason::summary

fn GateReason::summary(self : GateReason) -> String

#
GateResult

pub struct GateResult {
policy : GatePolicy
passed : Bool
risk_score : Int
reasons : Array[GateReason]
} derive(
Debug
)

#
GateResult::decision

fn GateResult::decision(self : GateResult) -> String

#
GateResult::has_reason

fn GateResult::has_reason(self : GateResult, code : String) -> Bool

#
GateResult::passed

fn GateResult::passed(self : GateResult) -> Bool

#
GateResult::policy

fn GateResult::policy(self : GateResult) -> GatePolicy

#
GateResult::reason_count

fn GateResult::reason_count(self : GateResult) -> Int

#
GateResult::reasons

fn GateResult::reasons(self : GateResult) -> Array[GateReason]

#
GateResult::risk_score

fn GateResult::risk_score(self : GateResult) -> Int

#
GateResult::text_report

fn GateResult::text_report(self : GateResult) -> String

#
HintKind

pub(all) enum HintKind {
MoveSpecificRule
TightenCidr
ConfirmDefaultPolicy
CanonicalizeInput
RemoveDuplicate
ReviewSpecialScope
FixSyntax
ReviewOverlap
} derive(Eq,
Debug
)

Category of remediation hint produced from audit findings.

#
HintKind::label

fn HintKind::label(self : HintKind) -> String

#
IPv4

pub struct IPv4 {
value : UInt
} derive(Eq,
Debug
)

A parsed IPv4 address stored as an unsigned 32-bit integer.

#
IPv4::is_limited_broadcast

fn IPv4::is_limited_broadcast(self : IPv4) -> Bool

fn IPv4::is_link_local(self : IPv4) -> Bool

#
IPv4::is_loopback

fn IPv4::is_loopback(self : IPv4) -> Bool

#
IPv4::is_multicast

fn IPv4::is_multicast(self : IPv4) -> Bool

#
IPv4::is_private

fn IPv4::is_private(self : IPv4) -> Bool

#
IPv4::is_public

fn IPv4::is_public(self : IPv4) -> Bool

Returns true when the address is not one of the private or special scopes currently recognized by this package.

#
IPv4::is_unspecified

fn IPv4::is_unspecified(self : IPv4) -> Bool

#
IPv4::new

fn IPv4::new(value : UInt) -> IPv4

#
IPv4::parse

fn IPv4::parse(input : String) -> Result[IPv4, String]

#
IPv4::scope_label

fn IPv4::scope_label(self : IPv4) -> String

#
IPv4::to_dotted

fn IPv4::to_dotted(self : IPv4) -> String

#
IPv4::value

fn IPv4::value(self : IPv4) -> UInt

#
ImportFormat

pub(all) enum ImportFormat {
PlainRules
NginxAccess
CsvRules
} derive(Eq,
Debug
)

Input syntax understood by the configuration import layer.

#
ImportFormat::label

fn ImportFormat::label(self : ImportFormat) -> String

#
ImportIssue

pub struct ImportIssue {
line : Int
level : ImportIssueLevel
code : String
message : String
input : String
} derive(Eq,
Debug
)

#
ImportIssue::code

fn ImportIssue::code(self : ImportIssue) -> String

#
ImportIssue::input

fn ImportIssue::input(self : ImportIssue) -> String

#
ImportIssue::level

#
ImportIssue::line

fn ImportIssue::line(self : ImportIssue) -> Int

#
ImportIssue::message

fn ImportIssue::message(self : ImportIssue) -> String

#
ImportIssue::new

fn ImportIssue::new(line : Int, level : ImportIssueLevel, code : String, message : String, input : String) -> ImportIssue

#
ImportIssue::summary

fn ImportIssue::summary(self : ImportIssue) -> String

#
ImportIssueLevel

pub(all) enum ImportIssueLevel {
ImportWarning
ImportError
} derive(Eq,
Debug
)

#
ImportIssueLevel::label

fn ImportIssueLevel::label(self : ImportIssueLevel) -> String

#
ImportReport

pub struct ImportReport {
format : ImportFormat
source_count : Int
rules : Array[Rule]
issues : Array[ImportIssue]
} derive(
Debug
)

#
ImportReport::accepted_count

fn ImportReport::accepted_count(self : ImportReport) -> Int

#
ImportReport::can_audit

fn ImportReport::can_audit(self : ImportReport) -> Bool

#
ImportReport::error_count

fn ImportReport::error_count(self : ImportReport) -> Int

#
ImportReport::format

fn ImportReport::format(self : ImportReport) -> ImportFormat

#
ImportReport::is_clean

fn ImportReport::is_clean(self : ImportReport) -> Bool

#
ImportReport::issues

#
ImportReport::rules

fn ImportReport::rules(self : ImportReport) -> Array[Rule]

#
ImportReport::source_count

fn ImportReport::source_count(self : ImportReport) -> Int

#
ImportReport::summary

fn ImportReport::summary(self : ImportReport) -> String

#
ImportReport::text_report

fn ImportReport::text_report(self : ImportReport) -> String

#
ImportReport::to_ruleset

fn ImportReport::to_ruleset(self : ImportReport) -> RuleSet

#
ImportReport::warning_count

fn ImportReport::warning_count(self : ImportReport) -> Int

#
Rule

pub struct Rule {
id : String
action : RuleAction
block : CidrBlock
source_block : String
note : String
} derive(Eq,
Debug
)

A single ordered firewall, proxy, or gateway rule.

#
Rule::action

fn Rule::action(self : Rule) -> RuleAction

#
Rule::block

fn Rule::block(self : Rule) -> CidrBlock

#
Rule::id

fn Rule::id(self : Rule) -> String

#
Rule::is_canonical

fn Rule::is_canonical(self : Rule) -> Bool

#
Rule::matches

fn Rule::matches(self : Rule, ip : IPv4) -> Bool

#
Rule::new

fn Rule::new(id : String, action : RuleAction, block : CidrBlock, source_block? : String, note? : String) -> Rule

#
Rule::note

fn Rule::note(self : Rule) -> String

#
Rule::parse

fn Rule::parse(id : String, line : String) -> Result[Rule, String]

#
Rule::same_decision

fn Rule::same_decision(self : Rule, other : Rule) -> Bool

#
Rule::source_block

fn Rule::source_block(self : Rule) -> String

#
Rule::summary

fn Rule::summary(self : Rule) -> String

#
RuleAction

pub(all) enum RuleAction {
Allow
Deny
} derive(Eq,
Debug
)

Action taken when a CIDR rule matches.

#
RuleAction::label

fn RuleAction::label(self : RuleAction) -> String

#
RuleAction::parse

fn RuleAction::parse(input : String) -> Result[RuleAction, String]

#
RuleChange

pub struct RuleChange {
kind : RuleChangeKind
before_id : String
after_id : String
block : String
message : String
} derive(Eq,
Debug
)

A change item matched by CIDR range.

#
RuleChange::after_id

fn RuleChange::after_id(self : RuleChange) -> String

#
RuleChange::before_id

fn RuleChange::before_id(self : RuleChange) -> String

#
RuleChange::block

fn RuleChange::block(self : RuleChange) -> String

#
RuleChange::kind

#
RuleChange::message

fn RuleChange::message(self : RuleChange) -> String

#
RuleChange::new

fn RuleChange::new(kind : RuleChangeKind, before_id : String, after_id : String, block : String, message : String) -> RuleChange

#
RuleChange::summary

fn RuleChange::summary(self : RuleChange) -> String

#
RuleChangeKind

pub(all) enum RuleChangeKind {
AddedRule
RemovedRule
ActionChanged
NoteChanged
UnchangedRule
} derive(Eq,
Debug
)

Type of change between two rule sets.

#
RuleChangeKind::is_risky

fn RuleChangeKind::is_risky(self : RuleChangeKind) -> Bool

#
RuleChangeKind::label

fn RuleChangeKind::label(self : RuleChangeKind) -> String

#
RuleSet

pub struct RuleSet {
rules : Array[Rule]
parse_errors : Array[String]
} derive(
Debug
)

#
RuleSet::audit

fn RuleSet::audit(self : RuleSet) -> AuditReport

#
RuleSet::audit_with_policy

fn RuleSet::audit_with_policy(self : RuleSet, policy : AuditPolicy) -> AuditReport

#
RuleSet::coverage_ranges

fn RuleSet::coverage_ranges(self : RuleSet, action : RuleAction) -> Array[AddressRange]

Returns the union of all rule blocks with the requested action.

This is a structural view. Ordered first-match behavior is intentionally handled separately by RuleSet::decide.

#
RuleSet::coverage_report

fn RuleSet::coverage_report(self : RuleSet, action : RuleAction, target : CidrBlock) -> String

#
RuleSet::coverage_stats

fn RuleSet::coverage_stats(self : RuleSet) -> CoverageStats

#
RuleSet::decide

fn RuleSet::decide(self : RuleSet, ip : IPv4) -> Decision

#
RuleSet::diff_from

fn RuleSet::diff_from(self : RuleSet, previous : RuleSet) -> DiffReport

#
RuleSet::evaluate_gate

fn RuleSet::evaluate_gate(self : RuleSet, audit_policy : AuditPolicy, gate_policy : GatePolicy) -> GateResult

#
RuleSet::explain_ip

fn RuleSet::explain_ip(self : RuleSet, ip : IPv4) -> String

#
RuleSet::explain_many

fn RuleSet::explain_many(self : RuleSet, probes : Array[String]) -> String

#
RuleSet::first_rule_for_block

fn RuleSet::first_rule_for_block(self : RuleSet, block : CidrBlock) -> String

#
RuleSet::from_lines

fn RuleSet::from_lines(lines : Array[String]) -> RuleSet

#
RuleSet::inventory_report

fn RuleSet::inventory_report(self : RuleSet) -> String

#
RuleSet::matching_rules

fn RuleSet::matching_rules(self : RuleSet, ip : IPv4) -> Array[Rule]

#
RuleSet::new

fn RuleSet::new(rules : Array[Rule]) -> RuleSet

#
RuleSet::parse_errors

fn RuleSet::parse_errors(self : RuleSet) -> Array[String]

#
RuleSet::rules

fn RuleSet::rules(self : RuleSet) -> Array[Rule]

#
Severity

pub(all) enum Severity {
Info
Warning
Critical
} derive(Eq,
Debug
)

Severity assigned to an audit finding.

#
Severity::label

fn Severity::label(self : Severity) -> String

#
Severity::score

fn Severity::score(self : Severity) -> Int

#
aggregate_blocks

fn aggregate_blocks(blocks : Array[CidrBlock]) -> AggregationReport

Aggregates already parsed CIDR blocks.

#
aggregate_cidr_strings

fn aggregate_cidr_strings(inputs : Array[String]) -> AggregationReport

Parses and aggregates a list of CIDR strings.

Invalid entries are preserved in errors rather than aborting the whole operation, making the API suitable for configuration linting.

#
audit_rules

fn audit_rules(rules : Array[Rule]) -> AuditReport

#
audit_rules_with_policy

fn audit_rules_with_policy(rules : Array[Rule], policy : AuditPolicy) -> AuditReport

#
clip_ranges_to_block

fn clip_ranges_to_block(target : CidrBlock, coverage : Array[AddressRange]) -> Array[AddressRange]

Clips address ranges to a target CIDR and returns a merged union.

#
diff_rule_sets

fn diff_rule_sets(current : RuleSet, previous : RuleSet) -> DiffReport

#
import_csv_rules

fn import_csv_rules(lines : Array[String]) -> ImportReport

Imports rows in action,cidr,note form.

A header row is optional. Quoted fields, embedded commas, and doubled quote escapes are supported. Extra columns are folded into the note and reported as a warning so information is not silently discarded.

#
import_nginx_access

fn import_nginx_access(lines : Array[String]) -> ImportReport

Imports Nginx access directives such as allow 10.0.0.0/8;.

deny all; and allow all; are converted to 0.0.0.0/0. Other Nginx directives are rejected so accidental input from an unrelated section is visible to the caller.

#
import_plain_rules

fn import_plain_rules(lines : Array[String]) -> ImportReport

Imports the native compact syntax: allow 10.0.0.0/8 note.

#
merge_address_ranges

fn merge_address_ranges(input : Array[AddressRange]) -> Array[AddressRange]

Sorts and merges overlapping or adjacent address ranges.

#
parse_rules

fn parse_rules(lines : Array[String]) -> RuleSet

#
ranges_from_blocks

fn ranges_from_blocks(blocks : Array[CidrBlock]) -> Array[AddressRange]

Converts CIDR blocks into a sorted, non-overlapping union of address ranges.

#
uncovered_ranges

fn uncovered_ranges(target : CidrBlock, coverage : Array[AddressRange]) -> Array[AddressRange]

Finds holes inside target that are not covered by any supplied range.