From b41dfdd4a27fc16b78f0d16ad27d5005ef8cefc1 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Sun, 21 Jun 2026 11:19:08 -0700 Subject: [PATCH] feat: validate Gateway= in .network files (config_parse_route_section gateway ltypes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Network] Gateway= and [Route] Gateway= mapped to config_parse_route_section with ltypes ROUTE_GATEWAY_NETWORK / ROUTE_GATEWAY, but neither ltype was registered, so Gateway values got no validation at all (resolved to NullOptionValue). This also showed up as the key staying unmarked under the new GrammarEngineKeyAnnotator. Add two grammar validators (reusing the existing IPV4_ADDR / IPV6_ADDR combinators): - ConfigParseRouteSectionGatewayNetworkOptionValue ([Network] Gateway=): IPv4/IPv6 address only — per systemd.network(5) the [Network] short-hand does not take the special tokens. - ConfigParseRouteSectionGatewayOptionValue ([Route] Gateway=): IPv4/IPv6 address or the special values "_dhcp4" / "_ipv6ra". Registered for ROUTE_GATEWAY_NETWORK and ROUTE_GATEWAY in AiGenerated. Works under the default engine and lights up under the experimental key marker (it's a GrammarOptionValue), confirming the pipeline end-to-end. Refs #467 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 2 + ...seRouteSectionGatewayNetworkOptionValue.kt | 21 ++++++++ ...nfigParseRouteSectionGatewayOptionValue.kt | 23 ++++++++ ...ParseRouteSectionGatewayOptionValueTest.kt | 54 +++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayNetworkOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRouteSectionGatewayOptionValueTest.kt diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt index acf10cd..39c2b6c 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt @@ -189,6 +189,8 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_restrict_network_interfaces", "0") to ConfigParseRestrictNetworkInterfacesOptionValue() as OptionValueInformation, Validator("config_parse_ring_buffer_or_channel", "0") to ConfigParseRingBufferOrChannelOptionValue() as OptionValueInformation, Validator("config_parse_route_prefix_preference", "0") to ConfigParseRoutePrefixPreferenceOptionValue() as OptionValueInformation, + Validator("config_parse_route_section", "ROUTE_GATEWAY") to ConfigParseRouteSectionGatewayOptionValue() as OptionValueInformation, + Validator("config_parse_route_section", "ROUTE_GATEWAY_NETWORK") to ConfigParseRouteSectionGatewayNetworkOptionValue() as OptionValueInformation, Validator("config_parse_route_section", "ROUTE_METRIC_FASTOPEN_NO_COOKIE") to ConfigParseRouteSectionOptionValue() as OptionValueInformation, Validator("config_parse_route_section", "ROUTE_METRIC_HOPLIMIT") to ConfigParseRouteSectionOptionValue() as OptionValueInformation, Validator("config_parse_route_section", "ROUTE_METRIC_INITRWND") to ConfigParseRouteSectionOptionValue() as OptionValueInformation, diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayNetworkOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayNetworkOptionValue.kt new file mode 100644 index 0000000..0e4e150 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayNetworkOptionValue.kt @@ -0,0 +1,21 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* + +/** + * Validator for [Network] Gateway= (config_parse_route_section, ltype ROUTE_GATEWAY_NETWORK). + * + * systemd.network(5): "The gateway address, which must be in the format described in inet_pton(3). + * This is a short-hand for a [Route] section only containing a Gateway= key." + * + * So it accepts an IPv4 or IPv6 address only — unlike [Route] Gateway=, the special "_dhcp4" / + * "_ipv6ra" tokens are NOT accepted in the [Network] short-hand. + */ +class ConfigParseRouteSectionGatewayNetworkOptionValue : SimpleGrammarOptionValues( + "config_parse_route_section", + SequenceCombinator( + AlternativeCombinator(IPV4_ADDR, IPV6_ADDR), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayOptionValue.kt new file mode 100644 index 0000000..d707664 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRouteSectionGatewayOptionValue.kt @@ -0,0 +1,23 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* + +/** + * Validator for [Route] Gateway= (config_parse_route_section, ltype ROUTE_GATEWAY). + * + * systemd.network(5): "Takes the gateway address or the special values "_dhcp4" and "_ipv6ra". If + * "_dhcp4" or "_ipv6ra" is set, then the gateway address provided by DHCPv4 or IPv6 RA is used." + */ +class ConfigParseRouteSectionGatewayOptionValue : SimpleGrammarOptionValues( + "config_parse_route_section", + SequenceCombinator( + AlternativeCombinator( + IPV4_ADDR, + IPV6_ADDR, + LiteralChoiceTerminal("_dhcp4"), + LiteralChoiceTerminal("_ipv6ra") + ), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRouteSectionGatewayOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRouteSectionGatewayOptionValueTest.kt new file mode 100644 index 0000000..2a549d2 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRouteSectionGatewayOptionValueTest.kt @@ -0,0 +1,54 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +/** + * Gateway= coverage for systemd .network files (config_parse_route_section, ltypes ROUTE_GATEWAY + * and ROUTE_GATEWAY_NETWORK). Previously unregistered, so Gateway values were not validated at all. + * + * [Network] Gateway= is an IPv4/IPv6 address only; [Route] Gateway= also accepts "_dhcp4"/"_ipv6ra". + */ +class ConfigParseRouteSectionGatewayOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidGateways() { + // language="unit file (systemd)" + val file = """ + [Network] + Gateway=192.168.1.1 + Gateway=2001:db8::1 + [Route] + Gateway=10.0.0.1 + Gateway=fe80::1 + Gateway=_dhcp4 + Gateway=_ipv6ra + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testInvalidGateways() { + // [Network] does not accept the special tokens; bad addresses are rejected in both sections; + // [Route] only accepts _dhcp4 / _ipv6ra (not _dhcp6). One highlight per line. + // language="unit file (systemd)" + val file = """ + [Network] + Gateway=_dhcp4 + Gateway=notanip + [Route] + Gateway=_dhcp6 + Gateway=300.1.2.3 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + + assertSize(4, myFixture.doHighlighting()) + } +}