From 87f5cbc9c1c162b8695c82c30ee568e28eaee553 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Sat, 20 Jun 2026 10:57:00 -0700 Subject: [PATCH] feat: add 7 validators - set_credential, load_credential, log_namespace, log_extra_fields, exec_selinux_context, exec_smack_process_label, unset_environ (Resolves #465) Each grammar is calibrated against the systemd C parser, biased toward false-negatives over false-positives (documented per-validator in KDoc): - config_parse_set_credential (ltypes 0,1): :; id is credential_name_valid (no '/' ':' control, not "."/".."), data arbitrary. - config_parse_load_credential (ltypes 0,1): [:]; id is credential_name_valid, source loosely constrained. - config_parse_log_namespace: log_namespace_name_valid (filename-safe, no '/', not "."/".."). - config_parse_log_extra_fields: list of FIELD=value, FIELD a journal field name [A-Z][A-Z0-9_]{0,63}. - config_parse_exec_selinux_context / config_parse_exec_smack_process_label: optional leading '-' then a specifier-validated string. - config_parse_unset_environ: list of env names or NAME=value assignments. Because the credential parsers cover ltypes 0 and 1, this clears 9 burn-down entries: OptionValueTest 366 -> 357 missing functions (1979 -> 2024 found). Each validator ships valid + invalid tests; full suite passes. Stacked on #464 (both touch AiGenerated.kt); merge #464 first. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 9 ++++ ...onfigParseExecSelinuxContextOptionValue.kt | 21 ++++++++++ ...igParseExecSmackProcessLabelOptionValue.kt | 21 ++++++++++ .../ConfigParseLoadCredentialOptionValue.kt | 24 +++++++++++ .../ConfigParseLogExtraFieldsOptionValue.kt | 31 ++++++++++++++ .../ai/ConfigParseLogNamespaceOptionValue.kt | 23 ++++++++++ .../ai/ConfigParseSetCredentialOptionValue.kt | 26 ++++++++++++ .../ai/ConfigParseUnsetEnvironOptionValue.kt | 29 +++++++++++++ ...gParseExecSelinuxContextOptionValueTest.kt | 42 +++++++++++++++++++ ...rseExecSmackProcessLabelOptionValueTest.kt | 42 +++++++++++++++++++ ...onfigParseLoadCredentialOptionValueTest.kt | 42 +++++++++++++++++++ ...onfigParseLogExtraFieldsOptionValueTest.kt | 42 +++++++++++++++++++ .../ConfigParseLogNamespaceOptionValueTest.kt | 41 ++++++++++++++++++ ...ConfigParseSetCredentialOptionValueTest.kt | 42 +++++++++++++++++++ .../ConfigParseUnsetEnvironOptionValueTest.kt | 42 +++++++++++++++++++ 15 files changed, 477 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSelinuxContextOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSmackProcessLabelOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLoadCredentialOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogExtraFieldsOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogNamespaceOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSetCredentialOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUnsetEnvironOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSelinuxContextOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSmackProcessLabelOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLoadCredentialOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogExtraFieldsOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogNamespaceOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSetCredentialOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnsetEnvironOptionValueTest.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 0a61160..acf10cd 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 @@ -10,6 +10,15 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai.* fun getAllAIGeneratedValidators(): Map { val allValidators = mapOf( Validator("config_parse_6rd_prefix", "0") to ConfigParse6rdPrefixOptionValue() as OptionValueInformation, + Validator("config_parse_set_credential", "0") to ConfigParseSetCredentialOptionValue() as OptionValueInformation, + Validator("config_parse_set_credential", "1") to ConfigParseSetCredentialOptionValue() as OptionValueInformation, + Validator("config_parse_load_credential", "0") to ConfigParseLoadCredentialOptionValue() as OptionValueInformation, + Validator("config_parse_load_credential", "1") to ConfigParseLoadCredentialOptionValue() as OptionValueInformation, + Validator("config_parse_log_namespace", "0") to ConfigParseLogNamespaceOptionValue() as OptionValueInformation, + Validator("config_parse_log_extra_fields", "0") to ConfigParseLogExtraFieldsOptionValue() as OptionValueInformation, + Validator("config_parse_exec_selinux_context", "0") to ConfigParseExecSelinuxContextOptionValue() as OptionValueInformation, + Validator("config_parse_exec_smack_process_label", "0") to ConfigParseExecSmackProcessLabelOptionValue() as OptionValueInformation, + Validator("config_parse_unset_environ", "0") to ConfigParseUnsetEnvironOptionValue() 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, diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSelinuxContextOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSelinuxContextOptionValue.kt new file mode 100644 index 0000000..cb8dde6 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSelinuxContextOptionValue.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 SELinuxContext=. + * + * C function: config_parse_exec_selinux_context in src/core/load-fragment.c. An optional leading + * '-' marks the assignment as "ignore failures"; the remainder is run through unit_full_printf() + * (specifier expansion) and stored verbatim -- there is no constraint on the context string + * itself. So, as with config_parse_unit_string_printf, the only meaningful check is specifier + * validity: every '%' must be '%%' or a valid unit specifier. The empty value resets the field. + */ +class ConfigParseExecSelinuxContextOptionValue : SimpleGrammarOptionValues( + "config_parse_exec_selinux_context", + SequenceCombinator( + RegexTerminal(".*", "-?(?:[^%]|%%|%[iIjJnNpPfyYcrRCdDELSthsaAbBHlqmMovwWgGuUTV])*"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSmackProcessLabelOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSmackProcessLabelOptionValue.kt new file mode 100644 index 0000000..44446ff --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSmackProcessLabelOptionValue.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 SmackProcessLabel=. + * + * C function: config_parse_exec_smack_process_label in src/core/load-fragment.c. Identical shape + * to SELinuxContext=: an optional leading '-' marks "ignore failures"; the remainder is run + * through unit_full_printf() and stored verbatim with no constraint on the label string. The only + * meaningful check is specifier validity ('%' must be '%%' or a valid unit specifier). The empty + * value resets the field. + */ +class ConfigParseExecSmackProcessLabelOptionValue : SimpleGrammarOptionValues( + "config_parse_exec_smack_process_label", + SequenceCombinator( + RegexTerminal(".*", "-?(?:[^%]|%%|%[iIjJnNpPfyYcrRCdDELSthsaAbBHlqmMovwWgGuUTV])*"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLoadCredentialOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLoadCredentialOptionValue.kt new file mode 100644 index 0000000..09f26c5 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLoadCredentialOptionValue.kt @@ -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 LoadCredential= (ltype 0) and LoadCredentialEncrypted= (ltype 1). + * + * C function: config_parse_load_credential in src/core/load-fragment.c. The value is "" or + * ":": the id must satisfy credential_name_valid() (non-empty, not "."/"..", no '/', + * no ':', no control, <= NAME_MAX); the optional source is either an absolute (normalized) path or + * another credential name. The source is left loosely constrained (any non-control text) to avoid + * false positives. + * + * Composed as id + optional(':' + source) so an invalid id localizes to the id. + */ +class ConfigParseLoadCredentialOptionValue : SimpleGrammarOptionValues( + "config_parse_load_credential", + SequenceCombinator( + RegexTerminal("[^:]+", "(?!\\.\\.?(?::|$))[^/:\\x00-\\x1F]{1,255}"), + ZeroOrOne(SequenceCombinator(LiteralChoiceTerminal(":"), RegexTerminal(".*", "[^\\x00-\\x1F]*"))), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogExtraFieldsOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogExtraFieldsOptionValue.kt new file mode 100644 index 0000000..6b0071d --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogExtraFieldsOptionValue.kt @@ -0,0 +1,31 @@ +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 LogExtraFields=. + * + * C function: config_parse_log_extra_fields in src/core/load-fragment.c. The value is a + * whitespace-separated list of "FIELD=value" entries; each FIELD must be a valid journal field + * name (journal_field_valid, src/libsystemd/sd-journal): 1-64 characters, only A-Z 0-9 '_', not + * starting with a digit or '_'. The value after '=' is arbitrary (binary-safe). Each entry must + * contain a '='. + * + * Composed from per-token combinators (field name, '=', value) repeated over whitespace so that an + * invalid entry localizes to that entry rather than invalidating the whole value. + */ +private val LOG_EXTRA_FIELD_ENTRY = SequenceCombinator( + RegexTerminal("[^=\\s]+", "[A-Z][A-Z0-9_]{0,63}"), + LiteralChoiceTerminal("="), + RegexTerminal("[^\\s]*", "[^\\s]*") +) + +class ConfigParseLogExtraFieldsOptionValue : SimpleGrammarOptionValues( + "config_parse_log_extra_fields", + SequenceCombinator( + LOG_EXTRA_FIELD_ENTRY, + ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), LOG_EXTRA_FIELD_ENTRY)), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogNamespaceOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogNamespaceOptionValue.kt new file mode 100644 index 0000000..4f10546 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseLogNamespaceOptionValue.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 LogNamespace=. + * + * C function: config_parse_log_namespace in src/core/load-fragment.c -> log_namespace_name_valid() + * in src/basic/syslog-util.c. After specifier expansion the value must be string_is_safe with + * STRING_FILENAME semantics (no '/', not "."/"..", no control characters), a valid unit instance + * name, and <= LOG_NAMESPACE_MAX characters. + * + * Grammar: a single token of non-control, non-'/' characters (not "."/".."), <= 255 chars; '%' + * is allowed for specifier syntax. The empty value resets the field. + */ +class ConfigParseLogNamespaceOptionValue : SimpleGrammarOptionValues( + "config_parse_log_namespace", + SequenceCombinator( + RegexTerminal(".+", "(?!\\.\\.?$)[^/\\x00-\\x1F]{1,255}"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSetCredentialOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSetCredentialOptionValue.kt new file mode 100644 index 0000000..5e09651 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSetCredentialOptionValue.kt @@ -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 SetCredential= (ltype 0) and SetCredentialEncrypted= (ltype 1). + * + * C function: config_parse_set_credential in src/core/load-fragment.c. The value is ":": + * extract_first_word(sep ":") yields the id, which must satisfy credential_name_valid() + * (= filename_is_valid && fdname_is_valid, i.e. non-empty, not "."/"..", no '/', no ':', no + * control characters, <= NAME_MAX); the remainder is the credential data (an arbitrary + * C-unescaped string for ltype 0, Base64 for ltype 1). The data itself is left unconstrained (so + * invalid Base64 is a false-negative rather than a false-positive). The empty value resets the list. + * + * Composed as id + ':' + data so an invalid id localizes to the id rather than the whole value. + */ +class ConfigParseSetCredentialOptionValue : SimpleGrammarOptionValues( + "config_parse_set_credential", + SequenceCombinator( + RegexTerminal("[^:]+", "(?!\\.\\.?:)[^/:\\x00-\\x1F]{1,255}"), + LiteralChoiceTerminal(":"), + RegexTerminal(".*", ".*"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUnsetEnvironOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUnsetEnvironOptionValue.kt new file mode 100644 index 0000000..8bffd48 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUnsetEnvironOptionValue.kt @@ -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 UnsetEnvironment=. + * + * C function: config_parse_unset_environ in src/core/load-fragment.c. The value is a + * whitespace-separated list where each entry must be either a valid environment variable name + * (env_name_is_valid) or a full assignment (env_assignment_is_valid). An environment variable + * name matches [A-Za-z_][A-Za-z0-9_]*; an assignment is "NAME=value" with an arbitrary value. + * + * Composed from per-token combinators (name, optional '=' value) repeated over whitespace so that + * an invalid entry localizes to that entry rather than invalidating the whole value. + */ +private val UNSET_ENVIRON_ENTRY = SequenceCombinator( + RegexTerminal("[^=\\s]+", "[A-Za-z_][A-Za-z0-9_]*"), + ZeroOrOne(SequenceCombinator(LiteralChoiceTerminal("="), RegexTerminal("[^\\s]*", "[^\\s]*"))) +) + +class ConfigParseUnsetEnvironOptionValue : SimpleGrammarOptionValues( + "config_parse_unset_environ", + SequenceCombinator( + UNSET_ENVIRON_ENTRY, + ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), UNSET_ENVIRON_ENTRY)), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSelinuxContextOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSelinuxContextOptionValueTest.kt new file mode 100644 index 0000000..9e05ab1 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSelinuxContextOptionValueTest.kt @@ -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 ConfigParseExecSelinuxContextOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + SELinuxContext=system_u:system_r:httpd_t:s0 + SELinuxContext=-system_u:object_r:foo_t + SELinuxContext=context for %i + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Unknown specifier (%e) and a trailing bare '%'. + // language=unit file (systemd) + val file = """ + [Service] + SELinuxContext=bad %e specifier + SELinuxContext=ends with % + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSmackProcessLabelOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSmackProcessLabelOptionValueTest.kt new file mode 100644 index 0000000..e519f3b --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSmackProcessLabelOptionValueTest.kt @@ -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 ConfigParseExecSmackProcessLabelOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + SmackProcessLabel=my-label + SmackProcessLabel=-optional-label + SmackProcessLabel=label-%i + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Unknown specifier (%e) and a trailing bare '%'. + // language=unit file (systemd) + val file = """ + [Service] + SmackProcessLabel=bad %e label + SmackProcessLabel=ends with % + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLoadCredentialOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLoadCredentialOptionValueTest.kt new file mode 100644 index 0000000..c33b3b3 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLoadCredentialOptionValueTest.kt @@ -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 ConfigParseLoadCredentialOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + LoadCredential=mycred:/etc/mycred + LoadCredentialEncrypted=other:/run/creds/other + LoadCredential=justid + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // A '/' in the id and a ".." id are rejected by credential_name_valid. + // language=unit file (systemd) + val file = """ + [Service] + LoadCredential=bad/id:/secret + LoadCredentialEncrypted=..:/secret + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogExtraFieldsOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogExtraFieldsOptionValueTest.kt new file mode 100644 index 0000000..a076201 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogExtraFieldsOptionValueTest.kt @@ -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 ConfigParseLogExtraFieldsOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + LogExtraFields=FIELD=value + LogExtraFields=PRIORITY=6 MESSAGE_ID=abc + LogExtraFields=EMPTY= + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Journal field names must be uppercase and each entry needs a '='. + // language=unit file (systemd) + val file = """ + [Service] + LogExtraFields=lowercase=value + LogExtraFields=NOEQUALS + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogNamespaceOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogNamespaceOptionValueTest.kt new file mode 100644 index 0000000..a99f293 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseLogNamespaceOptionValueTest.kt @@ -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 ConfigParseLogNamespaceOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + LogNamespace=myns + LogNamespace=log-ns_1 + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // log_namespace_name_valid rejects '/' and "..". + // language=unit file (systemd) + val file = """ + [Service] + LogNamespace=bad/ns + LogNamespace=.. + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSetCredentialOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSetCredentialOptionValueTest.kt new file mode 100644 index 0000000..c20b779 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSetCredentialOptionValueTest.kt @@ -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 ConfigParseSetCredentialOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + SetCredential=mycred:some value + SetCredentialEncrypted=other:Zm9vYmFy + SetCredential=withcolons:a:b:c + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // No ':' separator, and a '/' in the credential id, are invalid. + // language=unit file (systemd) + val file = """ + [Service] + SetCredential=nocolon + SetCredential=bad/id:value + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnsetEnvironOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnsetEnvironOptionValueTest.kt new file mode 100644 index 0000000..30eff0f --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnsetEnvironOptionValueTest.kt @@ -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 ConfigParseUnsetEnvironOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language=unit file (systemd) + val file = """ + [Service] + UnsetEnvironment=FOO + UnsetEnvironment=FOO BAR=baz + UnsetEnvironment=_PRIVATE PATH=/usr/bin + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Names cannot start with a digit or contain '-'. + // language=unit file (systemd) + val file = """ + [Service] + UnsetEnvironment=1BAD + UnsetEnvironment=has-dash + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +}