diff --git a/lib/generators.nix b/lib/generators.nix index 4317e49c2538f..376aa4081bf4f 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -70,6 +70,7 @@ let split toJSON typeOf + escapeXML ; ## -- HELPER FUNCTIONS & DEFAULTS -- @@ -548,13 +549,17 @@ in rec { # Inputs - Options - : Empty set, there may be configuration options in the future + Structured function argument + + : escape (optional, default: `false`) + : If this option is true, XML special characters are escaped in string values and keys Value : The value to be converted to Plist */ - toPlist = {}: v: let + toPlist = { + escape ? false + }: v: let expr = ind: x: if x == null then "" else if isBool x then bool ind x else @@ -568,10 +573,12 @@ in rec { literal = ind: x: ind + x; + maybeEscapeXML = if escape then escapeXML else x: x; + bool = ind: x: literal ind (if x then "" else ""); int = ind: x: literal ind "${toString x}"; - str = ind: x: literal ind "${x}"; - key = ind: x: literal ind "${x}"; + str = ind: x: literal ind "${maybeEscapeXML x}"; + key = ind: x: literal ind "${maybeEscapeXML x}"; float = ind: x: literal ind "${toString x}"; indent = ind: expr "\t${ind}"; @@ -597,7 +604,10 @@ in rec { (expr "\t${ind}" value) ]) x)); - in '' + in + # TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11. + lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505) "Using `lib.generators.toPlist` without `escape = true` is deprecated" + '' ${expr "" v} diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 72b3d630b8da8..2128cf17a3334 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -1635,7 +1635,7 @@ runTests { expected = "«foo»"; }; - testToPlist = { + testToPlistUnescaped = { expr = mapAttrs (const (generators.toPlist { })) { value = { nested.values = { @@ -1651,10 +1651,34 @@ runTests { emptylist = []; attrs = { foo = null; "foo b/ar" = "baz"; }; emptyattrs = {}; + "keys are not " = "and < neither are string values"; }; }; }; - expected = { value = builtins.readFile ./test-to-plist-expected.plist; }; + expected = { value = builtins.readFile ./test-to-plist-unescaped-expected.plist; }; + }; + + testToPlistEscaped = { + expr = mapAttrs (const (generators.toPlist { escape = true; })) { + value = { + nested.values = { + int = 42; + float = 0.1337; + bool = true; + emptystring = ""; + string = "fn\${o}\"r\\d"; + newlinestring = "\n"; + path = /. + "/foo"; + null_ = null; + list = [ 3 4 "test" ]; + emptylist = []; + attrs = { foo = null; "foo b/ar" = "baz"; }; + emptyattrs = {}; + "keys are " = "and < so are string values"; + }; + }; + }; + expected = { value = builtins.readFile ./test-to-plist-escaped-expected.plist; }; }; testToLuaEmptyAttrSet = { diff --git a/lib/tests/test-to-plist-escaped-expected.plist b/lib/tests/test-to-plist-escaped-expected.plist new file mode 100644 index 0000000000000..6ccf05aecc43b --- /dev/null +++ b/lib/tests/test-to-plist-escaped-expected.plist @@ -0,0 +1,48 @@ + + + + + nested + + values + + attrs + + foo b/ar + baz + + bool + + emptyattrs + + + + emptylist + + + + emptystring + + float + 0.133700 + int + 42 + keys are <escaped> + and < so are string values + list + + 3 + 4 + test + + newlinestring + + + path + /foo + string + fn${o}"r\d + + + + \ No newline at end of file diff --git a/lib/tests/test-to-plist-expected.plist b/lib/tests/test-to-plist-unescaped-expected.plist similarity index 90% rename from lib/tests/test-to-plist-expected.plist rename to lib/tests/test-to-plist-unescaped-expected.plist index df0528a60767b..ea8d95699f731 100644 --- a/lib/tests/test-to-plist-expected.plist +++ b/lib/tests/test-to-plist-unescaped-expected.plist @@ -27,6 +27,8 @@ 0.133700 int 42 + keys are not + and < neither are string values list 3