Skip to content

Commit f9b82da

Browse files
ISSUE-#1064 Prevent crash caused by TOMLKit Parsing error
- Prevents TOMLKit table header parsing error from crashing AeroSpace by doing a pre-check for invalid table headers. - Additionally throws a helpful user error showing the line number + content of the invalid config line
1 parent f6876d2 commit f9b82da

File tree

1 file changed

+22
-1
lines changed

1 file changed

+22
-1
lines changed

Sources/AppBundle/config/parseConfig.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import AppKit
22
import Common
33
import HotKey
44
import TOMLKit
5+
import Foundation
56

67
func readConfig(forceConfigUrl: URL? = nil) -> Result<(Config, URL), String> {
78
let customConfigUrl: URL
@@ -30,7 +31,7 @@ func readConfig(forceConfigUrl: URL? = nil) -> Result<(Config, URL), String> {
3031
}
3132
}
3233

33-
enum TomlParseError: Error, CustomStringConvertible, Equatable {
34+
enum TomlParseError: Error, CustomStringConvertible, Equatable, LocalizedError {
3435
case semantic(_ backtrace: TomlBacktrace, _ message: String)
3536
case syntax(_ message: String)
3637

@@ -41,6 +42,9 @@ enum TomlParseError: Error, CustomStringConvertible, Equatable {
4142
case .syntax(let message): message
4243
}
4344
}
45+
var errorDescription: String? {
46+
return description
47+
}
4448
}
4549

4650
typealias ParsedToml<T> = Result<T, TomlParseError>
@@ -153,9 +157,26 @@ func parseCommandOrCommands(_ raw: TOMLValueConvertible) -> Parsed<[any Command]
153157
}
154158
}
155159

160+
// Note: this is only required due to a TOML parsing bug in TOMLKit which throws SIGABORT in parsing failures
161+
// Should be removed once TOMLKit is patched
162+
private func validateTOMLTableHeaderSyntax(in input: String) throws {
163+
// Matches [[ followed by at least one more [ While ignoring commented lines #
164+
let pattern = #"^(?!\s*#)\s*\[\[(?=\[)"#
165+
let regex = try NSRegularExpression(pattern: pattern, options: .anchorsMatchLines)
166+
let lines = input.components(separatedBy: .newlines)
167+
168+
for (index, line) in lines.enumerated() {
169+
let range = NSRange(location: 0, length: line.utf16.count)
170+
if regex.firstMatch(in: line, options: [], range: range) != nil {
171+
throw TomlParseError.syntax("TOML Parsing Error: Invalid Table Header \n→ expected a header string but found '[' \n→ Location: Line \(index + 1) \n→ Content: \(line)")
172+
}
173+
}
174+
}
175+
156176
func parseConfig(_ rawToml: String) -> (config: Config, errors: [TomlParseError]) { // todo change return value to Result
157177
let rawTable: TOMLTable
158178
do {
179+
try validateTOMLTableHeaderSyntax(in: rawToml)
159180
rawTable = try TOMLTable(string: rawToml)
160181
} catch let e as TOMLParseError {
161182
return (defaultConfig, [.syntax(e.debugDescription)])

0 commit comments

Comments
 (0)