Skip to content

Commit cfaded7

Browse files
committed
sanitize remote message
1 parent 3c99912 commit cfaded7

File tree

5 files changed

+136
-8
lines changed

5 files changed

+136
-8
lines changed

modules/term/fmt_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"testing"
7+
"unicode"
78
)
89

910
func TestStripAnsi(t *testing.T) {
@@ -15,3 +16,36 @@ func TestStripAnsi(t *testing.T) {
1516
func TestCygwinTerminal(t *testing.T) {
1617
fmt.Fprintf(os.Stderr, "IsCygwinTerminal: %v\n", IsCygwinTerminal(os.Stderr.Fd()))
1718
}
19+
20+
func TestSanitized(t *testing.T) {
21+
ss := []string{
22+
"error: Have you \033[31mread\033[m this?\a\n",
23+
fmt.Sprintf("\x1b[38;2;254;225;64m* %s jack\x1b[0m", os.Args[0]),
24+
}
25+
for i, s := range ss {
26+
s1 := SanitizeANSI(s, true)
27+
s2 := SanitizeANSI(s, false)
28+
fmt.Fprintf(os.Stderr, "round %d\n%s\x1b[0m\n%s\x1b[0m\n", i, s1, s2)
29+
}
30+
}
31+
32+
func TestTable(t *testing.T) {
33+
table := make([]int, 0, 256)
34+
for i := 0; i < 256; i++ {
35+
if unicode.IsControl(rune(i)) {
36+
table = append(table, CHAR_CONTROL)
37+
continue
38+
}
39+
if unicode.IsDigit(rune(i)) || i == ';' || i == ':' {
40+
table = append(table, CHAR_COLOR_SEQUENCE)
41+
continue
42+
}
43+
table = append(table, CHAR_UNSPECIFIED)
44+
}
45+
for i, b := range table {
46+
if i%16 == 0 && i != 0 {
47+
fmt.Fprintf(os.Stderr, "\n")
48+
}
49+
fmt.Fprintf(os.Stderr, "%d,", b)
50+
}
51+
}

modules/term/sanitized.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package term
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
)
8+
9+
const (
10+
CHAR_UNSPECIFIED = 0
11+
CHAR_COLOR_SEQUENCE = 1
12+
CHAR_CONTROL = 2
13+
)
14+
15+
var (
16+
charIndex = []byte{
17+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
18+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
19+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
21+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
25+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
26+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
27+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33+
}
34+
)
35+
36+
// https://github.com/gitgitgadget/git/pull/1853
37+
// https://public-inbox.org/git/Z4bqMYKRP7Gva5St@tapette.crustytoothpaste.net/T/#t
38+
func handleAnsiColorSequence(b *strings.Builder, text []byte, allowColor bool) int {
39+
/*
40+
* Valid ANSI color sequences are of the form
41+
*
42+
* ESC [ [<n> [; <n>]*] m
43+
*/
44+
if len(text) < 3 || text[0] != '\x1b' || text[1] != '[' {
45+
return 0
46+
}
47+
for i := 2; i < len(text); i++ {
48+
c := text[i]
49+
if c == 'm' {
50+
if allowColor {
51+
_, _ = b.Write(text[:i+1])
52+
}
53+
return i
54+
}
55+
if charIndex[c] != CHAR_COLOR_SEQUENCE {
56+
break
57+
}
58+
}
59+
return 0
60+
}
61+
62+
func SanitizeANSI(content string, allowColor bool) string {
63+
b := &strings.Builder{}
64+
text := []byte(content)
65+
b.Grow(len(text))
66+
for i := 0; i < len(text); i++ {
67+
c := text[i]
68+
if charIndex[c] != CHAR_CONTROL || c == '\t' || c == '\n' {
69+
_ = b.WriteByte(c)
70+
continue
71+
}
72+
if j := handleAnsiColorSequence(b, text[i:], allowColor); j != 0 {
73+
i += j
74+
continue
75+
}
76+
_ = b.WriteByte('^')
77+
_ = b.WriteByte(c + 0x40)
78+
}
79+
return b.String()
80+
}
81+
82+
func SanitizedF(format string, a ...any) (int, error) {
83+
content := fmt.Sprintf(format, a...)
84+
return os.Stderr.WriteString(SanitizeANSI(content, StderrLevel != LevelNone))
85+
}

pkg/transport/http/base.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,14 @@ func parseError(resp *http.Response) error {
216216
if err := json.NewDecoder(resp.Body).Decode(ec); err != nil {
217217
return fmt.Errorf("decode json error: %w", err)
218218
}
219-
ec.Message = strings.TrimRightFunc(ec.Message, unicode.IsSpace)
219+
ec.Message = term.SanitizeANSI(strings.TrimRightFunc(ec.Message, unicode.IsSpace), true)
220220
return ec
221221
}
222222
b, err := streamio.ReadMax(resp.Body, 1024)
223223
if err != nil {
224224
return &ErrorCode{status: resp.StatusCode, Message: fmt.Sprintf("%d %s\nError: %v", resp.StatusCode, resp.Status, err)}
225225
}
226-
body := strings.TrimRightFunc(string(b), unicode.IsSpace)
226+
body := term.SanitizeANSI(strings.TrimRightFunc(string(b), unicode.IsSpace), true)
227227
return &ErrorCode{status: resp.StatusCode, Message: fmt.Sprintf("%s\n%s", resp.Status, body)}
228228
}
229229

pkg/zeta/odb/pack.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/antgroup/hugescm/modules/crc"
1515
"github.com/antgroup/hugescm/modules/plumbing"
1616
"github.com/antgroup/hugescm/modules/plumbing/format/pktline"
17+
"github.com/antgroup/hugescm/modules/term"
1718
"github.com/antgroup/hugescm/modules/zeta/backend"
1819
"github.com/antgroup/hugescm/pkg/progress"
1920
)
@@ -136,6 +137,14 @@ type Report struct {
136137
Reason string
137138
}
138139

140+
func sanitizeLine(s string) string {
141+
p := strings.IndexByte(s, '\n')
142+
if p != -1 {
143+
s = s[:p]
144+
}
145+
return term.SanitizeANSI(strings.TrimSpace(s), term.StderrLevel != term.LevelNone)
146+
}
147+
139148
func (d *ODB) OnReport(ctx context.Context, refname plumbing.ReferenceName, reader io.Reader) (result *Report, err error) {
140149
var b strings.Builder
141150
r := pktline.NewScanner(io.TeeReader(reader, &b))
@@ -149,12 +158,12 @@ func (d *ODB) OnReport(ctx context.Context, refname plumbing.ReferenceName, read
149158
line := string(r.Bytes())
150159
pos := strings.IndexByte(line, ' ')
151160
if pos == -1 {
152-
return nil, fmt.Errorf("bad report line: %s", line)
161+
return nil, fmt.Errorf("bad report line: %s", sanitizeLine(line))
153162
}
154163
lab := line[0:pos]
155164
substr := line[pos+1:]
156165
if lab == "rate" {
157-
fmt.Fprintf(os.Stderr, "\x1b[2K\rremote: %s", strings.TrimSpace(substr))
166+
fmt.Fprintf(os.Stderr, "\x1b[2K\rremote: %s", sanitizeLine(substr))
158167
newLine = true
159168
continue
160169
}
@@ -165,7 +174,7 @@ func (d *ODB) OnReport(ctx context.Context, refname plumbing.ReferenceName, read
165174
}
166175
if lab == "unpack" {
167176
if substr != "ok" {
168-
fmt.Fprintf(os.Stderr, "remote: unpack %s\n", substr)
177+
term.SanitizedF("remote: unpack %s\n", substr)
169178
result = &Report{ReferenceName: refname, Reason: substr, Rejected: true}
170179
break
171180
}
@@ -190,7 +199,7 @@ func (d *ODB) OnReport(ctx context.Context, refname plumbing.ReferenceName, read
190199
break
191200
}
192201
if lab == "status" {
193-
fmt.Fprintf(os.Stderr, "remote: %s\n", line[pos+1:])
202+
term.SanitizedF("remote: %s\n", line[pos+1:])
194203
continue
195204
}
196205
}

pkg/zeta/push.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ func (r *Repository) doPushRemove(ctx context.Context, target plumbing.Reference
168168
if result.Rejected {
169169
sv := strengthen.StrSplitSkipEmpty(result.Reason, 2, '\n')
170170
for _, s := range sv {
171-
fmt.Fprintf(os.Stderr, "remote: %s\n", s)
171+
term.Fprintf(os.Stderr, "remote: %s\n", s)
172172
}
173-
fmt.Fprintf(os.Stderr, "To: %s\n \x1b[31m! [remote rejected]\x1b[0m %s (delete)\n", cleanedRemote, target.Short())
173+
term.Fprintf(os.Stderr, "To: %s\n \x1b[31m! [remote rejected]\x1b[0m %s (delete)\n", cleanedRemote, target.Short())
174174
error_red("failed to push some refs to '%s'", cleanedRemote)
175175
return errors.New(result.Reason)
176176
}

0 commit comments

Comments
 (0)