Skip to content

Section-scoped checks

A presence check answers “is this line anywhere in the config?”. Many standards need a narrower question: “is this line inside this interface?”, or “does every access port have it?”. rConfig has four ways to scope a check to a section. They find and bound sections differently, so pick by config style and by whether one section or every section must comply.

MethodSections checkedLine orderSection boundaryMinimum version
match_code_block_strictEvery section whose first line matches the anchorAnyIndentation, ! or }8.2.2
evaluate_block with scope:Every section whose first line matches the scopeAnyIndentation and !, or brace depth8.3.0
match_code_block_from_arrayThe first match onlyExact, consecutiveThe number of lines in the body8.0.0
match_code_block_flexibleThe first match onlyAnyBrace counting (for { } configs)8.0.0

For indentation-style configs such as IOS-XE, use match_code_block_strict or evaluate_block. Avoiding false positives shows what goes wrong with match_code_block_flexible on IOS-XE.


The first body line is the anchor. It must match at least one line in the config, or the rule fails. The remaining lines must all appear inside the section that the anchor starts, in any order. A section ends at the next line indented no deeper than the anchor, at a line that is only !, or at a line that is only }.

If the anchor uses a wildcard and matches several sections, every matched section must contain the remaining lines.

Body lines can be literal text, can use * and ? wildcards, or can start with re: for a regular expression. Matching is case-insensitive unless you write a delimited re:/.../ pattern without the i flag.

The examples use this two-port config (sanitised from tests/storage/policydata/match_code_block_flexible_interfaces.txt):

interface GigabitEthernet5/0/48
description ACCESS-PORT
switchport access vlan 206
switchport mode access
switchport voice vlan 3755
switchport port-security maximum 100
switchport port-security violation restrict
switchport port-security aging time 5
switchport port-security aging type inactivity
switchport port-security
ip access-group ACL-USER-OUT out
no logging event link-status
ipv6 traffic-filter port_client in
no snmp trap link-status
spanning-tree portfast
!
interface GigabitEthernet6/0/48
description ACCESS-PORT
switchport access vlan 145
switchport mode access
switchport voice vlan 3788
switchport port-security maximum 100
switchport port-security violation restrict
switchport port-security aging time 5
switchport port-security aging type inactivity
switchport port-security
ip access-group ACL-USER-OUT out
no logging event link-status
ipv6 traffic-filter port_client in
no snmp trap link-status
spanning-tree portfast
// Port 5/0/48 is a locked-down access port
#[match_code_block_strict]
interface GigabitEthernet5/0/48
switchport access vlan 206
switchport voice vlan 3755
switchport port-security maximum 100
spanning-tree portfast
// Port 5/0/48 is in access VLAN 145
#[match_code_block_strict]
interface GigabitEthernet5/0/48
switchport access vlan 145
// Every GigabitEthernet port carries the access-port baseline
#[match_code_block_strict]
interface GigabitEthernet*
description ACCESS-PORT
switchport mode access
switchport port-security maximum 100
switchport port-security violation restrict
switchport port-security aging time 5
switchport port-security aging type inactivity
switchport port-security
ip access-group ACL-USER-OUT out
no logging event link-status
ipv6 traffic-filter port_client in
no snmp trap link-status
spanning-tree portfast
// Every GigabitEthernet port uses voice VLAN 3755
#[match_code_block_strict]
interface GigabitEthernet*
switchport voice vlan 3755
// Every TenGigabitEthernet port is an access port
#[match_code_block_strict]
interface TenGigabitEthernet*
switchport mode access
// No anchor line
#[match_code_block_strict]
switchport port-security maximum 100
spanning-tree portfast
// Ports 5/0/x are in access VLAN 206 (regex anchor)
#[match_code_block_strict]
re:^interface\s+GigabitEthernet5/0/\d+$
switchport access vlan 206
RuleResultWhy
Port 5/0/48 locked downpassAll four lines are inside the 5/0/48 section.
Port 5/0/48 in VLAN 145failVLAN 145 is on 6/0/48. Lines from another section do not count.
Every port has the baselinepassBoth sections contain every line.
Every port uses voice VLAN 3755fail6/0/48 uses 3788. One non-compliant section fails the rule.
Every TenGigabitEthernet portfailThe anchor matches nothing. Zero sections is a failure, not a pass.
No anchor linefailThe first line becomes the anchor, and its “section” holds no other lines.
Regex anchorpassre: anchors work like literal ones.

Source: CustomerUseCases/MatchCodeBlockStrictTest. Minimum version 8.2.2.


evaluate_block takes a YAML body. A scope: key holds a regular expression for the first line of each section. The rule under it is evaluated once per matching section, and passes only if every section passes. A scope that matches no section fails. When a rule fails, rConfig records the first line of each failing section.

Sections whose first line contains { run until the braces balance, however deeply they nest. Other sections end at a line indented no deeper than the first line, or at a line that is only !.

Inside the rule, all: needs every item to pass, any: needs at least one, and not: inverts its item. A plain item is a config line, matched case-insensitively as part of any line in the section. equals: needs a whole line to match exactly.

This F5 BIG-IP example (sanitised from the F5 WAF tests) checks that every WAF-enabled virtual server carries four logging profiles:

// F5 WAF logging: every WAF-enabled virtual server carries the required log profiles (disabled servers are skipped)
#[evaluate_block]
scope: ^ltm virtual
any:
- equals: disabled
- not: ASM_WAF_
- all:
- siem_log_collector
- netlog_tcp
- Log illegal requests
- telemetry_asm_security_log_profile

Read the any: as: a server complies if it is disabled, or it has no WAF policy, or it has all four profiles.

Passing config:

ltm virtual /Common/vs_ok {
destination /Common/192.0.2.10:443
profiles {
/Common/ASM_WAF_policy_prod { }
/Common/siem_log_collector { }
/Common/netlog_tcp { }
/Common/Log illegal requests { }
/Common/telemetry_asm_security_log_profile { }
}
source 0.0.0.0/0
}

Failing config: vs_good complies and vs_no_waf has no WAF policy, but vs_bad has a WAF policy and only two of the four profiles.

ltm virtual /Common/vs_good {
destination /Common/192.0.2.10:443
profiles {
/Common/ASM_WAF_policy_prod { }
/Common/siem_log_collector { }
/Common/netlog_tcp { }
/Common/Log illegal requests { }
/Common/telemetry_asm_security_log_profile { }
}
source 0.0.0.0/0
}
ltm virtual /Common/vs_no_waf {
destination /Common/192.0.2.10:443
profiles {
}
source 0.0.0.0/0
}
ltm virtual /Common/vs_bad {
destination /Common/192.0.2.10:443
profiles {
/Common/ASM_WAF_policy_prod { }
/Common/siem_log_collector { }
/Common/netlog_tcp { }
}
source 0.0.0.0/0
}
ConfigRule resultFailing section recorded
Passing configpassNone
Failing configfailltm virtual /Common/vs_bad { (only this one)

Source: CustomerUseCases/F5WafBlockComplianceCustomerTest::test_waf_logging_passes_when_a_waf_server_has_all_required_log_profiles and test_waf_logging_fails_on_one_bad_block_among_many_and_names_it. Minimum version 8.3.0. The exemptions in this rule are covered in per-device values and exceptions.


The body is a run of consecutive lines. rConfig finds the first config line matching the first body line, then checks that each following config line matches the next body line, in order, with nothing in between. Wildcards and re: lines work as in match_code_block_strict.

// GigabitEthernet1 uses DHCP with auto negotiation
#[match_code_block_from_array]
interface GigabitEthernet1
ip address dhcp
negotiation auto
// Third line differs
#[match_code_block_from_array]
interface GigabitEthernet1
ip address dhcp
negotiation auto1
// Wildcards in a code block
#[match_code_block_from_array]
* GigabitEthernet1
ip address *
negotiation auto
// First line not found
#[match_code_block_from_array]
interface GigabitEthernet22
ip address dhcp
negotiation auto

Against the IOS-XE excerpt on must contain and must not contain, which holds:

interface GigabitEthernet1
ip address dhcp
negotiation auto
RuleResult
Exact three linespass
Third line differsfail
Wildcardspass
First line not foundfail

Order matters. On this switch config, inserting a line the config does not have in the middle of the body breaks the match:

// VLAN 10 block in this exact order
#[match_code_block_from_array]
vlan 10
name "Trusted"
untagged *
tagged *
ip access-group "TRUSTED-IN" in
// VLAN 10 block with an extra line in the middle
#[match_code_block_from_array]
vlan 10
name "Trusted"
untagged *
something else
tagged *
ip access-group "TRUSTED-IN" in
hostname switch1
!
vlan 10
name "Trusted"
untagged C17
tagged A6, B21, B3,
ip access-group "TRUSTED-IN" in
!
RuleResult
Exact orderpass
Extra line in the middlefail

Source: PolicyMethodsTest code block tests. Minimum version 8.0.0.


The first body line finds the start of the section. rConfig then counts braces to find where it ends, and checks that every body line appears somewhere inside it, in any order. It is built for brace-style configs such as Junos. Only the first matching section is checked.

// LLDP enabled on all interfaces with a management address
#[match_code_block_flexible]
protocols {
lldp {
management-address *;
interface all {
enable;
protocols {
lldp {
management-address 192.0.2.1;
interface all {
enable;
}
}
}

Result: pass. Source: CustomerUseCases/LldpCodeBlockPolicyMethodsTest::test_lldp_minimal_required_configuration.

The same test file asserts two failures. A config with OSPF and BGP but no LLDP fails this four-line rule:

// LLDP configured with a management address
#[match_code_block_flexible]
protocols {
lldp {
management-address *;
interface all {
protocols {
ospf {
area 0.0.0.0 {
interface all {
hello-interval 10;
}
}
}
bgp {
group external {
type external;
peer-as 65001;
neighbor 192.0.2.2;
}
}
}

Result: fail (test_lldp_negative_cases). And LLDP without a management address fails this rule:

// LLDP has a management address and enabled interfaces
#[match_code_block_flexible]
protocols {
lldp {
management-address *;
interface * {
enable;
protocols {
lldp {
port-id-subtype interface-name;
interface ge-0/0/0 {
enable;
}
}
}

Result: fail (test_lldp_incomplete_configuration). Minimum version 8.0.0.