From ccbc725f2d678255df1bd326fa511a492aa3a0aa Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Wed, 14 Feb 2024 17:18:36 -0800 Subject: [PATCH] html/template: escape additional tokens in MarshalJSON errors Escape " Reviewed-by: Damien Neil --- src/html/template/js.go | 22 ++++++++- src/html/template/js_test.go | 96 ++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/html/template/js.go b/src/html/template/js.go index b159af8e4bf..d911ada26d3 100644 --- a/src/html/template/js.go +++ b/src/html/template/js.go @@ -171,13 +171,31 @@ func jsValEscaper(args ...any) string { // cyclic data. This may be an unacceptable DoS risk. b, err := json.Marshal(a) if err != nil { - // Put a space before comment so that if it is flush against + // While the standard JSON marshaller does not include user controlled + // information in the error message, if a type has a MarshalJSON method, + // the content of the error message is not guaranteed. Since we insert + // the error into the template, as part of a comment, we attempt to + // prevent the error from either terminating the comment, or the script + // block itself. + // + // In particular we: + // * replace "*/" comment end tokens with "* /", which does not + // terminate the comment + // * replace " 1 so this loses precision in JS // but it is still a representable integer literal. - {uint64(1)<<53 + 1, " 9007199254740993 "}, - {float32(1.0), " 1 "}, - {float32(-1.0), " -1 "}, - {float32(0.5), " 0.5 "}, - {float32(-0.5), " -0.5 "}, - {float32(1.0) / float32(256), " 0.00390625 "}, - {float32(0), " 0 "}, - {math.Copysign(0, -1), " -0 "}, - {float64(1.0), " 1 "}, - {float64(-1.0), " -1 "}, - {float64(0.5), " 0.5 "}, - {float64(-0.5), " -0.5 "}, - {float64(0), " 0 "}, - {math.Copysign(0, -1), " -0 "}, - {"", `""`}, - {"foo", `"foo"`}, + {uint64(1)<<53 + 1, " 9007199254740993 ", false}, + {float32(1.0), " 1 ", false}, + {float32(-1.0), " -1 ", false}, + {float32(0.5), " 0.5 ", false}, + {float32(-0.5), " -0.5 ", false}, + {float32(1.0) / float32(256), " 0.00390625 ", false}, + {float32(0), " 0 ", false}, + {math.Copysign(0, -1), " -0 ", false}, + {float64(1.0), " 1 ", false}, + {float64(-1.0), " -1 ", false}, + {float64(0.5), " 0.5 ", false}, + {float64(-0.5), " -0.5 ", false}, + {float64(0), " 0 ", false}, + {math.Copysign(0, -1), " -0 ", false}, + {"", `""`, false}, + {"foo", `"foo"`, false}, // Newlines. - {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, + {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`, false}, // "\v" == "v" on IE 6 so use "\u000b" instead. - {"\t\x0b", `"\t\u000b"`}, - {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, - {[]any{}, "[]"}, - {[]any{42, "foo", nil}, `[42,"foo",null]`}, - {[]string{""}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`}, - {"", `"--\u003e"`}, - {"", `"]]\u003e"`}, - {"", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`, false}, + {"", `"--\u003e"`, false}, + {"", `"]]\u003e"`, false}, + {"