Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai.*
fun getAllAIGeneratedValidators(): Map<Validator, OptionValueInformation> {
val allValidators = mapOf(
Validator("config_parse_6rd_prefix", "0") to ConfigParse6rdPrefixOptionValue() as OptionValueInformation,
Validator("config_parse_unit_string_printf", "0") to ConfigParseUnitStringPrintfOptionValue() as OptionValueInformation,
Validator("config_parse_ip_filter_bpf_progs", "0") to ConfigParseIpFilterBpfProgsOptionValue() as OptionValueInformation,
Validator("config_parse_user_group_compat", "0") to ConfigParseUserGroupCompatOptionValue() as OptionValueInformation,
Validator("config_parse_io_device_weight", "0") to ConfigParseIoDeviceWeightOptionValue() as OptionValueInformation,
Validator("config_parse_io_device_latency", "0") to ConfigParseIoDeviceLatencyOptionValue() as OptionValueInformation,
Validator("config_parse_delegate_subgroup", "0") to ConfigParseDelegateSubgroupOptionValue() as OptionValueInformation,
Validator("config_parse_ad_actor_sys_prio", "0") to ConfigParseAdActorSysPrioOptionValue() as OptionValueInformation,
Validator("config_parse_ad_user_port_key", "0") to ConfigParseAdUserPortKeyOptionValue() as OptionValueInformation,
Validator("config_parse_address_families", "0") to ConfigParseAddressFamiliesOptionValue() as OptionValueInformation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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 DelegateSubgroup=.
*
* C function: config_parse_delegate_subgroup in src/core/load-fragment.c. The value is used
* verbatim (no specifier expansion) and must satisfy !cg_needs_escape() (src/basic/cgroup-util.c):
* it must be a valid filename (no '/', not "."/"..", <= NAME_MAX), must not start with '_' or '.',
* must not be one of "notify_on_release"/"release_agent"/"tasks", and must not start with
* "cgroup.".
*
* Grammar models all of those except the rarer "<controller>." rule (e.g. "cpu.") which we leave
* unenforced to avoid coupling to the kernel controller list / false positives. First character
* must not be '/', '.', '_' or a control char; subsequent characters must not be '/' or control;
* length <= 255.
*/
class ConfigParseDelegateSubgroupOptionValue : SimpleGrammarOptionValues(
"config_parse_delegate_subgroup",
SequenceCombinator(
RegexTerminal(
".+",
"(?!cgroup\\.)(?!(?:tasks|release_agent|notify_on_release)$)[^/._\\x00-\\x1F][^/\\x00-\\x1F]{0,254}"
),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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 IODeviceLatencyTargetSec=.
*
* C function: config_parse_io_device_latency in src/core/load-fragment.c. The value is
* "<path> <latency>": the first whitespace-separated word is a device path (specifier-expanded,
* path_simplify_and_warn with flags 0 -- not required to be absolute), and the remainder is a
* time span parsed by parse_sec().
*
* Grammar: a non-whitespace path token, whitespace, then a time span: one or more
* "<number><optional unit>" components (units per systemd time syntax), or "infinity". The number
* may be fractional. The path is left unconstrained to match the lenient C parsing.
*/
class ConfigParseIoDeviceLatencyOptionValue : SimpleGrammarOptionValues(
"config_parse_io_device_latency",
SequenceCombinator(
RegexTerminal("\\S+", "\\S+"),
WhitespaceTerminal(),
RegexTerminal(
".+",
"infinity|(?:\\d+(?:\\.\\d+)?\\s*(?:nsec|ns|usec|us|msec|ms|seconds|second|sec|s|minutes|minute|min|hours|hour|hr|h|days|day|d|weeks|week|w|months|month|M|years|year|y|m)?\\s*)+"
),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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 IODeviceWeight=.
*
* C function: config_parse_io_device_weight in src/core/load-fragment.c. The value is
* "<path> <weight>": the first whitespace-separated word is a device path (specifier-expanded,
* path_simplify_and_warn with flags 0 -- NOT required to be absolute), and the remainder is parsed
* by cg_weight_parse(), which accepts an integer in [CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX] =
* [1, 10000] (src/basic/cgroup-util.h).
*
* Grammar: a non-whitespace path token, whitespace, then an integer 1..10000. The path is left
* unconstrained (any token) to match the lenient C parsing and avoid false positives.
*/
class ConfigParseIoDeviceWeightOptionValue : SimpleGrammarOptionValues(
"config_parse_io_device_weight",
SequenceCombinator(
RegexTerminal("\\S+", "\\S+"),
WhitespaceTerminal(),
IntegerTerminal(1, 10001),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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 IPIngressFilterPath= / IPEgressFilterPath=.
*
* C function: config_parse_ip_filter_bpf_progs in src/core/load-fragment.c. It runs
* unit_path_printf() (specifier expansion) and then path_simplify_and_warn(PATH_CHECK_ABSOLUTE),
* i.e. the resolved value must be an absolute path.
*
* Grammar: the value must start with '/' (an absolute path) or '%' (a specifier that may resolve
* to an absolute path, e.g. %d/...); the remainder may be any non-control characters. Specifiers
* are accepted loosely here (we do not enumerate the path-specifier set), since for a path the
* meaningful, low-false-positive check is "must be absolute". The empty value resets the list.
*/
class ConfigParseIpFilterBpfProgsOptionValue : SimpleGrammarOptionValues(
"config_parse_ip_filter_bpf_progs",
SequenceCombinator(
RegexTerminal(".+", "(?:/|%)[^\\x00-\\x1F]*"),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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 the ~35 keys parsed by config_parse_unit_string_printf (Unit.Description,
* Service.SyslogIdentifier, Service.PAMName, Socket.SmackLabel, Mount.Type, Mount.Options, ...).
*
* C function: config_parse_unit_string_printf in src/core/load-fragment.c. It runs
* unit_full_printf() (specifier expansion) and then config_parse_string() (store as-is). There is
* therefore NO constraint on the resulting value beyond it being an ordinary (UTF-8) string -- so
* the only thing we can meaningfully validate is the specifier syntax: every '%' must be either a
* literal "%%" or a known unit specifier. unit_full_printf() returns an error (and the assignment
* is dropped) on an unknown specifier, so flagging those is faithful to systemd.
*
* Valid specifiers are the unit table plus COMMON_SYSTEM/CREDS/TMP_SPECIFIERS from
* src/core/unit-printf.c and src/shared/specifier.h:
* i I j J n N p P f y Y c r R C d D E L S t h s a A b B H l q m M o v w W g G u U T V
*
* Any other character (including non-ASCII, e.g. "Description=café") is accepted. The empty value
* (which resets the field) is also accepted.
*/
class ConfigParseUnitStringPrintfOptionValue : SimpleGrammarOptionValues(
"config_parse_unit_string_printf",
SequenceCombinator(
RegexTerminal(".*", "(?:[^%]|%%|%[iIjJnNpPfyYcrRCdDELSthsaAbBHlqmMovwWgGuUTV])*"),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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 User= / Group= (and similar) keys parsed by config_parse_user_group_compat.
*
* C function: config_parse_user_group_compat in src/core/load-fragment.c. It runs unit_full_printf()
* (specifier expansion) and then valid_user_group_name() with
* VALID_USER_ALLOW_NUMERIC | VALID_USER_RELAX | VALID_USER_WARN.
*
* In RELAX mode (see src/basic/user-util.c) the name is checked only superficially: it must be
* non-empty, valid UTF-8, contain no control characters, contain no ':' or '/', and have no
* leading/trailing whitespace (interior whitespace is allowed). Numeric IDs are allowed. We model
* exactly those rules and leave the rarer RELAX checks (purely-numeric-but-not-a-uid, "-N", "."/
* "..") unenforced rather than risk false positives. '%' is allowed (specifier syntax). The empty
* value resets the field.
*/
class ConfigParseUserGroupCompatOptionValue : SimpleGrammarOptionValues(
"config_parse_user_group_compat",
SequenceCombinator(
RegexTerminal(".+", "[^\\s:/\\x00-\\x1F](?:[^:/\\x00-\\x1F]*[^\\s:/\\x00-\\x1F])?"),
EOF()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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

class ConfigParseDelegateSubgroupOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language=unit file (systemd)
val file = """
[Service]
DelegateSubgroup=supervisor
DelegateSubgroup=my-subgroup
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// cg_needs_escape rejects names starting with '_'/'.' or containing '/', the "cgroup."
// prefix, and the reserved names tasks/release_agent/notify_on_release.
// language=unit file (systemd)
val file = """
[Service]
DelegateSubgroup=_leading-underscore
DelegateSubgroup=has/slash
DelegateSubgroup=cgroup.controllers
DelegateSubgroup=tasks
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(4, highlights)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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

class ConfigParseIoDeviceLatencyOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language=unit file (systemd)
val file = """
[Service]
IODeviceLatencyTargetSec=/dev/sda 10ms
IODeviceLatencyTargetSec=/dev/sdb 1s
IODeviceLatencyTargetSec=/dev/sdc 0
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// A non-time-span latency, and a path with no latency, are invalid.
// language=unit file (systemd)
val file = """
[Service]
IODeviceLatencyTargetSec=/dev/sda notatime
IODeviceLatencyTargetSec=/dev/sdb
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(2, highlights)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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

class ConfigParseIoDeviceWeightOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language=unit file (systemd)
val file = """
[Service]
IODeviceWeight=/dev/sda 100
IODeviceWeight=/dev/sdb 10000
IODeviceWeight=/dev/sdc 1
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// Weight must be in [1, 10000]; a path with no weight is also invalid.
// language=unit file (systemd)
val file = """
[Service]
IODeviceWeight=/dev/sda 0
IODeviceWeight=/dev/sda 20000
IODeviceWeight=/dev/sda
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(3, highlights)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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

class ConfigParseIpFilterBpfProgsOptionValueTest : AbstractUnitFileTest() {

@Test
fun testValidValues() {
// language=unit file (systemd)
val file = """
[Service]
IPIngressFilterPath=/sys/fs/bpf/my-prog
IPEgressFilterPath=%d/credential-prog
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(0, highlights)
}

@Test
fun testInvalidValues() {
// path_simplify_and_warn(PATH_CHECK_ABSOLUTE) rejects relative paths.
// language=unit file (systemd)
val file = """
[Service]
IPIngressFilterPath=relative/path
IPEgressFilterPath=./also-relative
""".trimIndent()

setupFileInEditor("file.service", file)
enableInspection(InvalidValueInspection::class.java)
val highlights = myFixture.doHighlighting()

assertSize(2, highlights)
}
}
Loading
Loading