mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-21 19:39:43 +01:00
fix #2099
Add optional support for rfc1459 and rfc1459-strict casemappings
This commit is contained in:
parent
5ee32cda1c
commit
7772b55cab
@ -134,9 +134,10 @@ server:
|
|||||||
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
|
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
|
||||||
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
|
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
|
||||||
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
|
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
|
||||||
# and 'permissive', which allows identifiers containing unusual characters like
|
# 'permissive', which allows identifiers containing unusual characters like
|
||||||
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
|
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
|
||||||
# client compatibility problems. we recommend leaving this value at its default;
|
# client compatibility problems, and the legacy mappings 'rfc1459' and
|
||||||
|
# 'rfc1459-strict'. we recommend leaving this value at its default;
|
||||||
# however, note that changing it once the network is already up and running is
|
# however, note that changing it once the network is already up and running is
|
||||||
# problematic.
|
# problematic.
|
||||||
casemapping: "ascii"
|
casemapping: "ascii"
|
||||||
|
@ -453,6 +453,10 @@ func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err err
|
|||||||
result = CasemappingPRECIS
|
result = CasemappingPRECIS
|
||||||
case "permissive", "fun":
|
case "permissive", "fun":
|
||||||
result = CasemappingPermissive
|
result = CasemappingPermissive
|
||||||
|
case "rfc1459":
|
||||||
|
result = CasemappingRFC1459
|
||||||
|
case "rfc1459-strict":
|
||||||
|
result = CasemappingRFC1459Strict
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid casemapping value: %s", orig)
|
return fmt.Errorf("invalid casemapping value: %s", orig)
|
||||||
}
|
}
|
||||||
@ -1622,7 +1626,16 @@ func (config *Config) generateISupport() (err error) {
|
|||||||
isupport.Initialize()
|
isupport.Initialize()
|
||||||
isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen))
|
isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen))
|
||||||
isupport.Add("BOT", "B")
|
isupport.Add("BOT", "B")
|
||||||
isupport.Add("CASEMAPPING", "ascii")
|
var casemappingToken string
|
||||||
|
switch config.Server.Casemapping {
|
||||||
|
default:
|
||||||
|
casemappingToken = "ascii" // this is published for ascii, precis, or permissive
|
||||||
|
case CasemappingRFC1459:
|
||||||
|
casemappingToken = "rfc1459"
|
||||||
|
case CasemappingRFC1459Strict:
|
||||||
|
casemappingToken = "rfc1459-strict"
|
||||||
|
}
|
||||||
|
isupport.Add("CASEMAPPING", casemappingToken)
|
||||||
isupport.Add("CHANLIMIT", fmt.Sprintf("%s:%d", chanTypes, config.Channels.MaxChannelsPerClient))
|
isupport.Add("CHANLIMIT", fmt.Sprintf("%s:%d", chanTypes, config.Channels.MaxChannelsPerClient))
|
||||||
isupport.Add("CHANMODES", chanmodesToken)
|
isupport.Add("CHANMODES", chanmodesToken)
|
||||||
if config.History.Enabled && config.History.ChathistoryMax > 0 {
|
if config.History.Enabled && config.History.ChathistoryMax > 0 {
|
||||||
|
@ -60,6 +60,10 @@ const (
|
|||||||
// confusables detection: standard skeleton algorithm (which may be ineffective
|
// confusables detection: standard skeleton algorithm (which may be ineffective
|
||||||
// over the larger set of permitted identifiers)
|
// over the larger set of permitted identifiers)
|
||||||
CasemappingPermissive
|
CasemappingPermissive
|
||||||
|
// rfc1459 is a legacy mapping as defined here: https://modern.ircdocs.horse/#casemapping-parameter
|
||||||
|
CasemappingRFC1459
|
||||||
|
// rfc1459-strict is a legacy mapping as defined here: https://modern.ircdocs.horse/#casemapping-parameter
|
||||||
|
CasemappingRFC1459Strict
|
||||||
)
|
)
|
||||||
|
|
||||||
// XXX this is a global variable without explicit synchronization.
|
// XXX this is a global variable without explicit synchronization.
|
||||||
@ -110,6 +114,10 @@ func casefoldWithSetting(str string, setting Casemapping) (string, error) {
|
|||||||
return foldASCII(str)
|
return foldASCII(str)
|
||||||
case CasemappingPermissive:
|
case CasemappingPermissive:
|
||||||
return foldPermissive(str)
|
return foldPermissive(str)
|
||||||
|
case CasemappingRFC1459:
|
||||||
|
return foldRFC1459(str, false)
|
||||||
|
case CasemappingRFC1459Strict:
|
||||||
|
return foldRFC1459(str, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +222,7 @@ func Skeleton(name string) (string, error) {
|
|||||||
switch globalCasemappingSetting {
|
switch globalCasemappingSetting {
|
||||||
default:
|
default:
|
||||||
return realSkeleton(name)
|
return realSkeleton(name)
|
||||||
case CasemappingASCII:
|
case CasemappingASCII, CasemappingRFC1459, CasemappingRFC1459Strict:
|
||||||
// identity function is fine because we independently case-normalize in Casefold
|
// identity function is fine because we independently case-normalize in Casefold
|
||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
@ -302,6 +310,23 @@ func foldASCII(str string) (result string, err error) {
|
|||||||
return strings.ToLower(str), nil
|
return strings.ToLower(str), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rfc1459Replacer = strings.NewReplacer("[", "{", "]", "}", "\\", "|", "~", "^")
|
||||||
|
rfc1459StrictReplacer = strings.NewReplacer("[", "{", "]", "}", "\\", "|")
|
||||||
|
)
|
||||||
|
|
||||||
|
func foldRFC1459(str string, strict bool) (result string, err error) {
|
||||||
|
asciiFold, err := foldASCII(str)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
replacer := rfc1459Replacer
|
||||||
|
if strict {
|
||||||
|
replacer = rfc1459StrictReplacer
|
||||||
|
}
|
||||||
|
return replacer.Replace(asciiFold), nil
|
||||||
|
}
|
||||||
|
|
||||||
func IsPrintableASCII(str string) bool {
|
func IsPrintableASCII(str string) bool {
|
||||||
for i := 0; i < len(str); i++ {
|
for i := 0; i < len(str); i++ {
|
||||||
// allow space here because it's technically printable;
|
// allow space here because it's technically printable;
|
||||||
|
@ -279,3 +279,31 @@ func TestFoldASCIIInvalid(t *testing.T) {
|
|||||||
t.Errorf("control characters should be invalid in identifiers")
|
t.Errorf("control characters should be invalid in identifiers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFoldRFC1459(t *testing.T) {
|
||||||
|
folder := func(str string) (string, error) {
|
||||||
|
return foldRFC1459(str, false)
|
||||||
|
}
|
||||||
|
tester := func(first, second string, equal bool) {
|
||||||
|
validFoldTester(first, second, equal, folder, t)
|
||||||
|
}
|
||||||
|
tester("shivaram", "SHIVARAM", true)
|
||||||
|
tester("shivaram[a]", "shivaram{a}", true)
|
||||||
|
tester("shivaram\\a]", "shivaram{a}", false)
|
||||||
|
tester("shivaram\\a]", "shivaram|a}", true)
|
||||||
|
tester("shivaram~a]", "shivaram^a}", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFoldRFC1459Strict(t *testing.T) {
|
||||||
|
folder := func(str string) (string, error) {
|
||||||
|
return foldRFC1459(str, true)
|
||||||
|
}
|
||||||
|
tester := func(first, second string, equal bool) {
|
||||||
|
validFoldTester(first, second, equal, folder, t)
|
||||||
|
}
|
||||||
|
tester("shivaram", "SHIVARAM", true)
|
||||||
|
tester("shivaram[a]", "shivaram{a}", true)
|
||||||
|
tester("shivaram\\a]", "shivaram{a}", false)
|
||||||
|
tester("shivaram\\a]", "shivaram|a}", true)
|
||||||
|
tester("shivaram~a]", "shivaram^a}", false)
|
||||||
|
}
|
||||||
|
@ -108,9 +108,10 @@ server:
|
|||||||
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
|
# the recommended default is 'ascii' (traditional ASCII-only identifiers).
|
||||||
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
|
# the other options are 'precis', which allows UTF8 identifiers that are "sane"
|
||||||
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
|
# (according to UFC 8265), with additional mitigations for homoglyph attacks,
|
||||||
# and 'permissive', which allows identifiers containing unusual characters like
|
# 'permissive', which allows identifiers containing unusual characters like
|
||||||
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
|
# emoji, at the cost of increased vulnerability to homoglyph attacks and potential
|
||||||
# client compatibility problems. we recommend leaving this value at its default;
|
# client compatibility problems, and the legacy mappings 'rfc1459' and
|
||||||
|
# 'rfc1459-strict'. we recommend leaving this value at its default;
|
||||||
# however, note that changing it once the network is already up and running is
|
# however, note that changing it once the network is already up and running is
|
||||||
# problematic.
|
# problematic.
|
||||||
casemapping: "ascii"
|
casemapping: "ascii"
|
||||||
|
Loading…
Reference in New Issue
Block a user