Skip to content

Commit 2e5b9f2

Browse files
authored
Make SimpleSymbols safe for directly using in URLs (#1)
Signed-off-by: Tamal Saha <[email protected]>
1 parent c567252 commit 2e5b9f2

File tree

2 files changed

+68
-15
lines changed

2 files changed

+68
-15
lines changed

password.go

+40-11
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ const (
1111
Uppercase Charset = 1 << iota // 1 << 0 which is 00000001
1212
Lowercase // 1 << 1 which is 00000010
1313
Numbers // 1 << 2 which is 00000100
14-
Symbols // 1 << 3 which is 00001000
15-
SimpleSymbols // 1 << 4 which is 00001000
14+
Unreserved // 1 << 3 which is 00001000
15+
Reserved // 1 << 4 which is 00010000
16+
SimpleSymbols // 1 << 5 which is 00100000
17+
Symbols // 1 << 6 which is 01000000
18+
Default = Uppercase | Lowercase | Numbers | SimpleSymbols
1619
)
1720

1821
var (
@@ -22,14 +25,18 @@ var (
2225
len_lowercase = len(lowercase)
2326
numbers = []byte(`0123456789`)
2427
len_numbers = len(numbers)
28+
unreserved = []byte(`-._~`) // ref: https://perishablepress.com/stop-using-unsafe-characters-in-urls/
29+
len_unreserved = len(unreserved)
30+
reserved = []byte(`!#$&'()*+,/:;=?@[]`)
31+
len_reserved = len(reserved)
32+
simple_symbols = []byte(`!$&'()*+,-.:;=@_~`) // ref: https://github.com/golang/go/blob/release-branch.go1.15/src/net/url/url.go#L1158-L1186 , missing: Unreserved | Reserved - #/?[]
33+
len_simple_symbols = len(simple_symbols)
2534
symbols = []byte(`!"#$%&'()*+,-./:;<=>?@^[\]_{|}~` + "`")
2635
len_symbols = len(symbols)
27-
simple_symbols = []byte(`!#$%&*+-=?@^_|`)
28-
len_simple_symbols = len(simple_symbols)
2936
)
3037

3138
func Generate(n int) string {
32-
return GenerateForCharset(n, Uppercase|Lowercase|Numbers|SimpleSymbols)
39+
return GenerateForCharset(n, Default)
3340
}
3441

3542
func GenerateForCharset(n int, chset Charset) string {
@@ -45,12 +52,18 @@ func GenerateForCharset(n int, chset Charset) string {
4552
if chset&Numbers != 0 {
4653
count += len_numbers
4754
}
48-
if chset&Symbols != 0 {
49-
count += len_symbols
55+
if chset&Unreserved != 0 {
56+
count += len_unreserved
57+
}
58+
if chset&Reserved != 0 {
59+
count += len_reserved
5060
}
5161
if chset&SimpleSymbols != 0 {
5262
count += len_simple_symbols
5363
}
64+
if chset&Symbols != 0 {
65+
count += len_symbols
66+
}
5467
max := big.NewInt(int64(count))
5568

5669
for i := 0; i < n; i++ {
@@ -84,12 +97,20 @@ func GenerateForCharset(n int, chset Charset) string {
8497
idx -= len_numbers
8598
}
8699
}
87-
if chset&Symbols != 0 {
88-
if idx < len_symbols {
89-
buf[i] = symbols[idx]
100+
if chset&Unreserved != 0 {
101+
if idx < len_unreserved {
102+
buf[i] = unreserved[idx]
90103
continue
91104
} else {
92-
idx -= len_symbols
105+
idx -= len_unreserved
106+
}
107+
}
108+
if chset&Reserved != 0 {
109+
if idx < len_reserved {
110+
buf[i] = reserved[idx]
111+
continue
112+
} else {
113+
idx -= len_reserved
93114
}
94115
}
95116
if chset&SimpleSymbols != 0 {
@@ -100,6 +121,14 @@ func GenerateForCharset(n int, chset Charset) string {
100121
idx -= len_simple_symbols
101122
}
102123
}
124+
if chset&Symbols != 0 {
125+
if idx < len_symbols {
126+
buf[i] = symbols[idx]
127+
continue
128+
} else {
129+
idx -= len_symbols
130+
}
131+
}
103132
}
104133
return string(buf)
105134
}

password_test.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ func TestGenerateForCharset2(t *testing.T) {
5454
},
5555
},
5656
{
57-
name: "Uppercase | Lowercase | Symbols",
57+
name: "Uppercase | Lowercase | Unreserved",
5858
args: args{
5959
n: 8,
60-
chset: Uppercase | Lowercase | Symbols,
60+
chset: Uppercase | Lowercase | Unreserved,
61+
},
62+
},
63+
{
64+
name: "Uppercase | Lowercase | Reserved",
65+
args: args{
66+
n: 8,
67+
chset: Uppercase | Lowercase | Reserved,
6168
},
6269
},
6370
{
@@ -67,6 +74,13 @@ func TestGenerateForCharset2(t *testing.T) {
6774
chset: Uppercase | Lowercase | SimpleSymbols,
6875
},
6976
},
77+
{
78+
name: "Uppercase | Lowercase | Symbols",
79+
args: args{
80+
n: 8,
81+
chset: Uppercase | Lowercase | Symbols,
82+
},
83+
},
7084
}
7185
for _, tt := range tests {
7286
t.Run(tt.name, func(t *testing.T) {
@@ -98,8 +112,13 @@ func uses_charset(str string, chset Charset) bool {
98112
continue
99113
}
100114
}
101-
if chset&Symbols != 0 {
102-
if contains(symbols, data[i]) {
115+
if chset&Unreserved != 0 {
116+
if contains(unreserved, data[i]) {
117+
continue
118+
}
119+
}
120+
if chset&Reserved != 0 {
121+
if contains(reserved, data[i]) {
103122
continue
104123
}
105124
}
@@ -108,6 +127,11 @@ func uses_charset(str string, chset Charset) bool {
108127
continue
109128
}
110129
}
130+
if chset&Symbols != 0 {
131+
if contains(symbols, data[i]) {
132+
continue
133+
}
134+
}
111135
return false
112136
}
113137
return true

0 commit comments

Comments
 (0)