Section-scoped checks
Section-scoped checks
Section titled “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.
| Method | Sections checked | Line order | Section boundary | Minimum version |
|---|---|---|---|---|
match_code_block_strict | Every section whose first line matches the anchor | Any | Indentation, ! or } | 8.2.2 |
evaluate_block with scope: | Every section whose first line matches the scope | Any | Indentation and !, or brace depth | 8.3.0 |
match_code_block_from_array | The first match only | Exact, consecutive | The number of lines in the body | 8.0.0 |
match_code_block_flexible | The first match only | Any | Brace 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.
match_code_block_strict
Section titled “match_code_block_strict”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/48switchport access vlan 206switchport voice vlan 3755switchport port-security maximum 100spanning-tree portfast
// Port 5/0/48 is in access VLAN 145#[match_code_block_strict]interface GigabitEthernet5/0/48switchport access vlan 145
// Every GigabitEthernet port carries the access-port baseline#[match_code_block_strict]interface GigabitEthernet*description ACCESS-PORTswitchport mode accessswitchport port-security maximum 100switchport port-security violation restrictswitchport port-security aging time 5switchport port-security aging type inactivityswitchport port-securityip access-group ACL-USER-OUT outno logging event link-statusipv6 traffic-filter port_client inno snmp trap link-statusspanning-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 100spanning-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| Rule | Result | Why |
|---|---|---|
| Port 5/0/48 locked down | pass | All four lines are inside the 5/0/48 section. |
| Port 5/0/48 in VLAN 145 | fail | VLAN 145 is on 6/0/48. Lines from another section do not count. |
| Every port has the baseline | pass | Both sections contain every line. |
| Every port uses voice VLAN 3755 | fail | 6/0/48 uses 3788. One non-compliant section fails the rule. |
| Every TenGigabitEthernet port | fail | The anchor matches nothing. Zero sections is a failure, not a pass. |
| No anchor line | fail | The first line becomes the anchor, and its “section” holds no other lines. |
| Regex anchor | pass | re: anchors work like literal ones. |
Source: CustomerUseCases/MatchCodeBlockStrictTest. Minimum version 8.2.2.
evaluate_block with scope
Section titled “evaluate_block with scope”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 virtualany: - equals: disabled - not: ASM_WAF_ - all: - siem_log_collector - netlog_tcp - Log illegal requests - telemetry_asm_security_log_profileRead 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}| Config | Rule result | Failing section recorded |
|---|---|---|
| Passing config | pass | None |
| Failing config | fail | ltm 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.
match_code_block_from_array
Section titled “match_code_block_from_array”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 GigabitEthernet1ip address dhcpnegotiation auto
// Third line differs#[match_code_block_from_array]interface GigabitEthernet1ip address dhcpnegotiation auto1
// Wildcards in a code block#[match_code_block_from_array]* GigabitEthernet1ip address *negotiation auto
// First line not found#[match_code_block_from_array]interface GigabitEthernet22ip address dhcpnegotiation autoAgainst the IOS-XE excerpt on must contain and must not contain, which holds:
interface GigabitEthernet1 ip address dhcp negotiation auto| Rule | Result |
|---|---|
| Exact three lines | pass |
| Third line differs | fail |
| Wildcards | pass |
| First line not found | fail |
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 10name "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 10name "Trusted"untagged *something elsetagged *ip access-group "TRUSTED-IN" inhostname switch1!vlan 10 name "Trusted" untagged C17 tagged A6, B21, B3, ip access-group "TRUSTED-IN" in!| Rule | Result |
|---|---|
| Exact order | pass |
| Extra line in the middle | fail |
Source: PolicyMethodsTest code block tests. Minimum version 8.0.0.
match_code_block_flexible
Section titled “match_code_block_flexible”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.