Skip to content

Commit ddbacfd

Browse files
author
Steve Ramage
committed
Checkpoint
1 parent b9db697 commit ddbacfd

File tree

8 files changed

+443
-0
lines changed

8 files changed

+443
-0
lines changed

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/actions/NewUnitFileAction.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ class NewUnitFileAction : CreateFileFromTemplateAction(
2727
return "New Systemd Unit File"
2828
}
2929
}
30+
31+
32+
33+
// language=python
34+
val foo = """
35+
import foo
36+
""".trimIndent()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import foo

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class SemanticDataRepository private constructor() {
152152
validatorMap.putAll(CPUSharesOptionValue.validators)
153153
validatorMap.putAll(CgroupSocketBindOptionValue.validators)
154154
validatorMap.putAll(RlimitOptionValue.validators) // Scopes are not supported since they aren't standard unit files.
155+
validatorMap.putAll(NetworkAddressOptionValue.validators)
155156
fileClassToSectionNameToKeyValuesFromDoc["unit"]?.remove(SCOPE_KEYWORD)
156157
fileClassToSectionToKeyAndValidatorMap["unit"]?.remove(SCOPE_KEYWORD)
157158
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF
5+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.GrammarOptionValue
6+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IP_ADDR_AND_PREFIX_LENGTH
7+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator
8+
9+
class NetworkAddressOptionValue() : GrammarOptionValue("config_parse_address_section", GRAMMAR) {
10+
11+
companion object {
12+
val GRAMMAR = SequenceCombinator(
13+
IP_ADDR_AND_PREFIX_LENGTH, EOF())
14+
15+
val validators = mapOf(
16+
Validator("config_parse_address_section", "ADDRESS_ADDRESS") to NetworkAddressOptionValue()
17+
)
18+
}
19+
}
20+

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,49 @@ package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gra
33
val BYTES = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
44
val DEVICE = RegexTerminal("\\S+\\s*", "/[^\\u0000. ]+\\s*")
55
val IOPS = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
6+
7+
var IPV4_OCTET = IntegerTerminal(0, 256)
8+
val DOT = LiteralChoiceTerminal(".")
9+
var IPV4_ADDR = SequenceCombinator(IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET)
10+
11+
val CIDR_SEPARATOR = LiteralChoiceTerminal("/")
12+
13+
val IPV4_ADDR_AND_PREFIX_LENGTH = SequenceCombinator(IPV4_ADDR, CIDR_SEPARATOR, IntegerTerminal(8, 33))
14+
15+
var IPV6_HEXTET = RegexTerminal("[0-9a-fA-F]{1,4}", "[0-9a-fA-F]{1,4}")
16+
val COLON = LiteralChoiceTerminal(":")
17+
val DOUBLE_COLON = LiteralChoiceTerminal("::")
18+
19+
20+
val IPV6_FULL_SPECIFIED = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET)
21+
val IPV6_ZERO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 8)), IPV6_HEXTET)
22+
val IPV6_ONE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 7)), IPV6_HEXTET)
23+
val IPV6_TWO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 6)), IPV6_HEXTET)
24+
val IPV6_THREE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 5)), IPV6_HEXTET)
25+
val IPV6_FOUR_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 4)), IPV6_HEXTET)
26+
val IPV6_FIVE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 3)), IPV6_HEXTET)
27+
val IPV6_SIX_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 2)), IPV6_HEXTET)
28+
val IPV6_SEVEN_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(IPV6_HEXTET))
29+
val IPV6_ALL_ZEROS = DOUBLE_COLON
30+
31+
val IPV6_ADDR = AlternativeCombinator(
32+
IPV6_FULL_SPECIFIED,
33+
// Reverse the order of these
34+
IPV6_SEVEN_HEXTET_BEFORE_ZERO_COMP,
35+
IPV6_SIX_HEXTET_BEFORE_ZERO_COMP,
36+
IPV6_FIVE_HEXTET_BEFORE_ZERO_COMP,
37+
IPV6_FOUR_HEXTET_BEFORE_ZERO_COMP,
38+
IPV6_THREE_HEXTET_BEFORE_ZERO_COMP,
39+
IPV6_TWO_HEXTET_BEFORE_ZERO_COMP,
40+
IPV6_ONE_HEXTET_BEFORE_ZERO_COMP,
41+
// Must go last because it's the most general
42+
IPV6_ZERO_HEXTET_BEFORE_ZERO_COMP,
43+
IPV6_ALL_ZEROS,
44+
)
45+
46+
val IPV6_ADDR_AND_PREFIX_LENGTH = SequenceCombinator(IPV6_ADDR, CIDR_SEPARATOR, IntegerTerminal(64, 129))
47+
48+
49+
var IP_ADDR_AND_PREFIX_LENGTH = AlternativeCombinator(
50+
IPV4_ADDR_AND_PREFIX_LENGTH,
51+
IPV6_ADDR_AND_PREFIX_LENGTH)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar
2+
3+
import kotlin.math.max
4+
5+
/**
6+
* Repeat Combinator
7+
*/
8+
class Repeat(val combinator : Combinator, val minInclusive: Int, val maxExclusive: Int) : Combinator {
9+
10+
init {
11+
if (minInclusive < 0) {
12+
throw IllegalArgumentException("minInclusive must be >= 0")
13+
}
14+
if (maxExclusive < minInclusive) {
15+
throw IllegalArgumentException("maxExclusive must be >= minInclusive")
16+
}
17+
}
18+
19+
20+
private fun match(value: String, offset: Int, f: (String, Int) -> MatchResult): MatchResult {
21+
var index = offset
22+
val tokens = mutableListOf<String>()
23+
val terminals = mutableListOf<TerminalCombinator>()
24+
25+
var match = f(value, index)
26+
var matches = 0
27+
28+
if (match.matchResult == -1) {
29+
if (minInclusive != 0) {
30+
// This will return a match result = -1
31+
return match
32+
}
33+
34+
return MatchResult(tokens, offset, terminals, match.longestMatch)
35+
}
36+
37+
var maxLength = match.longestMatch
38+
39+
40+
while (match.matchResult != -1 && matches < maxExclusive) {
41+
matches++
42+
index = match.matchResult
43+
tokens.addAll(match.tokens)
44+
terminals.addAll(match.terminals)
45+
46+
match = f(value, index)
47+
maxLength = max(maxLength, match.longestMatch)
48+
}
49+
50+
if (matches < minInclusive) {
51+
return MatchResult(emptyList<String>(), -1, emptyList<TerminalCombinator>(), maxLength)
52+
} else {
53+
return MatchResult(tokens, index, terminals, maxLength)
54+
}
55+
}
56+
57+
override fun SyntacticMatch(value: String, offset: Int): MatchResult {
58+
return match(value, offset, combinator::SyntacticMatch)
59+
}
60+
61+
override fun SemanticMatch(value: String, offset: Int): MatchResult {
62+
return match(value, offset, combinator::SemanticMatch)
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.inspections
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
4+
5+
class InvalidValueForNetworkAddressesTest : AbstractUnitFileTest() {
6+
7+
fun testNoWarningWhenValidIPv4NetworkAddressSet() {
8+
// Fixture Setup
9+
// language="unit file (systemd)"
10+
val file="""
11+
[Network]
12+
Address=244.178.44.111/32
13+
""".trimIndent()
14+
15+
// Execute SUT
16+
setupFileInEditor("file.network", file)
17+
enableInspection(InvalidValueInspection::class.java)
18+
val highlights = myFixture.doHighlighting()
19+
20+
// Verification
21+
assertSize(0, highlights)
22+
}
23+
24+
fun testWarningWhenSomeInvalidIPv4NetworkAddressSet() {
25+
// Fixture Setup
26+
// language="unit file (systemd)"
27+
val file="""
28+
[Network]
29+
# The first octet is two high
30+
Address=256.178.44.111
31+
# Too few octets
32+
Address=245.124.2/12
33+
# Too many octets
34+
Address=1.2.3.4.5/8
35+
# Invalid Prefix Length
36+
Address=244.25.2.1/33
37+
# Invalid Prefix Length
38+
Address=244.25.2.1/7
39+
# No prefix
40+
Address=127.0.0.1
41+
""".trimIndent()
42+
43+
// Execute SUT
44+
setupFileInEditor("file.network", file)
45+
enableInspection(InvalidValueInspection::class.java)
46+
val highlights = myFixture.doHighlighting()
47+
48+
// Verification
49+
assertSize(6, highlights)
50+
51+
}
52+
53+
fun testNoWarningWhenValidIPv6NetworkAddressSet() {
54+
// Fixture Setup
55+
56+
/*
57+
Address=::ffff/64
58+
Address=::FFFF/64
59+
Address=::/64
60+
Address=::1/128
61+
Address=2001:db8::1/128
62+
Address=2001:db8:0:0:0:0:2:1/127
63+
Address=2001:db8:ABCD:0012::0/96
64+
Address=fe80::/64
65+
Address=::/128
66+
Address=::ffff:192.0.2.128/96
67+
Address=2001:0db8:85a3:0000:0000:8a2e:0370:7334/124
68+
Address=2001:0db8:85a3::8a2e:0370:7334/124
69+
Address=fd00:1234:5678:9abc:def0:1234:5678:9abc/100
70+
Address=2001:db8:0:0:0:0:0:1/126
71+
Address=2001:db8:85a3::8a2e:370:7334/120
72+
Address=::ABCD/112
73+
Address=::1/127
74+
Address=2001:db8::/65
75+
Address=ff02::1/128
76+
*/
77+
// language="unit file (systemd)"
78+
val file="""
79+
[Network]
80+
Address=2001:db8::/65
81+
82+
""".trimIndent()
83+
84+
// Execute SUT
85+
setupFileInEditor("file.network", file)
86+
enableInspection(InvalidValueInspection::class.java)
87+
val highlights = myFixture.doHighlighting()
88+
89+
// Verification
90+
assertSize(0, highlights)
91+
}
92+
93+
fun testWarningWhenSomeInvalidIPv6NetworkAddressSet() {
94+
// Fixture Setup
95+
// language="unit file (systemd)"
96+
val file="""
97+
[Network]
98+
# Too many hextets (9 instead of max 8)
99+
Address=2001:0db8:85a3:0000:0000:8a2e:0370:7334:1234/64
100+
# Too few hextets (only 2)
101+
Address=abcd:1234/64
102+
# Invalid character (g is not a hex digit)
103+
Address=2001:db8:85a3:0:0:8a2e:370g:7334/64
104+
# Double '::' not allowed
105+
Address=2001:db8::85a3::7334/64
106+
# Prefix too large (>128)
107+
Address=2001:db8::1/129
108+
# Prefix too small (<0)
109+
Address=2001:db8::1/-1
110+
# Missing prefix
111+
Address=2001:db8::1
112+
# Empty address
113+
Address=/64
114+
# Trailing colon
115+
Address=2001:db8:85a3:0:0:8a2e:370:7334:/64
116+
# Leading colon (not part of '::')
117+
Address=:2001:db8:85a3:0:0:8a2e:370:7334/64
118+
# Too many consecutive colons (illegal ':::')
119+
Address=2001:db8:::1/64
120+
# Embedded IPv4 with too many octets
121+
Address=::ffff:192.168.1.1.1/96
122+
# Embedded IPv4 with invalid octet (>255)
123+
Address=::ffff:300.168.1.1/96
124+
# Hextet too long (more than 4 hex digits)
125+
Address=2001:db8:12345::1/64
126+
# Non-hex character in hextet
127+
Address=2001:db8:zzzz::1/64
128+
""".trimIndent()
129+
130+
// Execute SUT
131+
setupFileInEditor("file.network", file)
132+
enableInspection(InvalidValueInspection::class.java)
133+
val highlights = myFixture.doHighlighting()
134+
135+
// Verification
136+
assertSize(15, highlights)
137+
138+
}
139+
140+
}

0 commit comments

Comments
 (0)