From b97f989032a25fca236508655193ab822019e252 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Feb 2026 23:37:20 +0200 Subject: [PATCH] federation/eventauth: add support for underscores in string power levels --- federation/eventauth/eventauth.go | 11 +++- .../eventauth/eventauth_internal_test.go | 61 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 federation/eventauth/eventauth_internal_test.go diff --git a/federation/eventauth/eventauth.go b/federation/eventauth/eventauth.go index eac110a3..d2073607 100644 --- a/federation/eventauth/eventauth.go +++ b/federation/eventauth/eventauth.go @@ -799,7 +799,7 @@ func parsePythonInt(val gjson.Result) *int { return ptr.Ptr(int(val.Int())) case gjson.String: // strconv.Atoi accepts signs as well as leading zeroes, so we just need to trim spaces beforehand - num, err := strconv.Atoi(strings.TrimSpace(val.Str)) + num, err := strconv.Atoi(removeUnderscores(strings.TrimSpace(val.Str))) if err != nil { return nil } @@ -810,6 +810,15 @@ func parsePythonInt(val gjson.Result) *int { } } +func removeUnderscores(num string) string { + numWithoutSign := strings.TrimPrefix(strings.TrimPrefix(num, "+"), "-") + if strings.HasPrefix(numWithoutSign, "_") || strings.HasSuffix(numWithoutSign, "_") { + // Leading or trailing underscores are not valid, let strconv.Atoi fail + return num + } + return strings.ReplaceAll(num, "_", "") +} + func safeParsePowerLevels(content jsontext.Value, into *event.PowerLevelsEventContent) { *into = event.PowerLevelsEventContent{ Users: make(map[id.UserID]int), diff --git a/federation/eventauth/eventauth_internal_test.go b/federation/eventauth/eventauth_internal_test.go new file mode 100644 index 00000000..e8f61b76 --- /dev/null +++ b/federation/eventauth/eventauth_internal_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2026 Tulir Asokan +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//go:build goexperiment.jsonv2 + +package eventauth + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" +) + +type pythonIntTest struct { + Name string + Input string + Expected int + Invalid bool +} + +var pythonIntTests = []pythonIntTest{ + {"True", `true`, 1, false}, + {"False", `false`, 0, false}, + {"SmallFloat", `3.1415`, 3, false}, + {"SmallFloatRoundDown", `10.999999999999999`, 10, false}, + {"SmallFloatRoundUp", `10.9999999999999999`, 11, false}, + {"BigFloatRoundDown", `1000000.9999999999`, 1000000, false}, + {"BigFloatRoundUp", `1000000.99999999999`, 1000001, false}, + {"String", `"123"`, 123, false}, + {"FloatInString", `"123.456"`, 0, true}, + {"StringWithPlusSign", `"+123"`, 123, false}, + {"StringWithMinusSign", `"-123"`, -123, false}, + {"StringWithSpaces", `" 123 "`, 123, false}, + {"StringWithSpacesAndSign", `" -123 "`, -123, false}, + {"StringWithUnderscores", `"123_456"`, 123456, false}, + {"StringWithUnderscores", `"123_456"`, 123456, false}, + {"StringWithTrailingUnderscore", `"123_456_"`, 0, true}, + {"StringWithLeadingUnderscore", `"_123_456"`, 0, true}, + {"StringWithUnderscoreAfterSign", `"+_123_456"`, 0, true}, + {"StringWithUnderscoreAfterSpace", `" _123_456"`, 0, true}, + {"StringWithUnderscoresAndSpaces", `" +1_2_3_4_5_6 "`, 123456, false}, +} + +func TestParsePythonInt(t *testing.T) { + for _, test := range pythonIntTests { + t.Run(test.Name, func(t *testing.T) { + output := parsePythonInt(gjson.Parse(test.Input)) + if test.Invalid { + assert.Nil(t, output) + } else { + require.NotNil(t, output) + assert.Equal(t, test.Expected, *output) + } + }) + } +}