Skip to content

Commit

Permalink
Merge pull request #7831 from ipfs/fix/escape-nonprintable-chars
Browse files Browse the repository at this point in the history
Escape non-printable characters in user output
  • Loading branch information
aschmahmann authored Jan 14, 2021
2 parents 0d63fbb + 20132a8 commit fb0a9ac
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 45 deletions.
2 changes: 1 addition & 1 deletion core/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ only-hash, and progress/status related flags) will change the final hash.
if quiet {
fmt.Fprintf(os.Stdout, "%s\n", output.Hash)
} else {
fmt.Fprintf(os.Stdout, "added %s %s\n", output.Hash, output.Name)
fmt.Fprintf(os.Stdout, "added %s %s\n", output.Hash, cmdenv.EscNonPrint(output.Name))
}

} else {
Expand Down
27 changes: 27 additions & 0 deletions core/commands/cmdenv/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmdenv

import (
"fmt"
"strconv"
"strings"

"github.com/ipfs/go-ipfs/commands"
Expand Down Expand Up @@ -70,3 +71,29 @@ func GetConfigRoot(env cmds.Environment) (string, error) {

return ctx.ConfigRoot, nil
}

// EscNonPrint converts non-printable characters and backslash into Go escape
// sequences. This is done to display all characters in a string, including
// those that would otherwise not be displayed or have an undesirable effect on
// the display.
func EscNonPrint(s string) string {
if !needEscape(s) {
return s
}

esc := strconv.Quote(s)
// Remove first and last quote, and unescape quotes.
return strings.ReplaceAll(esc[1:len(esc)-1], `\"`, `"`)
}

func needEscape(s string) bool {
if strings.ContainsRune(s, '\\') {
return true
}
for _, r := range s {
if !strconv.IsPrint(r) {
return true
}
}
return false
}
48 changes: 48 additions & 0 deletions core/commands/cmdenv/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmdenv

import (
"strconv"
"testing"
)

func TestEscNonPrint(t *testing.T) {
b := []byte("hello")
b[2] = 0x7f
s := string(b)
if !needEscape(s) {
t.Fatal("string needs escaping")
}
if !hasNonPrintable(s) {
t.Fatal("expected non-printable")
}
if hasNonPrintable(EscNonPrint(s)) {
t.Fatal("escaped string has non-printable")
}
if EscNonPrint(`hel\lo`) != `hel\\lo` {
t.Fatal("backslash not escaped")
}

s = `hello`
if needEscape(s) {
t.Fatal("string does not need escaping")
}
if EscNonPrint(s) != s {
t.Fatal("string should not have changed")
}
s = `"hello"`
if EscNonPrint(s) != s {
t.Fatal("string should not have changed")
}
if EscNonPrint(`"hel\"lo"`) != `"hel\\"lo"` {
t.Fatal("did not get expected escaped string")
}
}

func hasNonPrintable(s string) bool {
for _, r := range s {
if !strconv.IsPrint(r) {
return true
}
}
return false
}
3 changes: 2 additions & 1 deletion core/commands/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"

cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
ncmd "github.com/ipfs/go-ipfs/core/commands/name"
namesys "github.com/ipfs/go-ipfs/namesys"
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
Expand Down Expand Up @@ -77,7 +78,7 @@ The resolver can recursively resolve:
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ncmd.ResolvedPath) error {
fmt.Fprintln(w, out.Path.String())
fmt.Fprintln(w, cmdenv.EscNonPrint(out.Path.String()))
return nil
}),
},
Expand Down
8 changes: 4 additions & 4 deletions core/commands/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,9 @@ var keyRenameCmd = &cmds.Command{
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, kro *KeyRenameOutput) error {
if kro.Overwrite {
fmt.Fprintf(w, "Key %s renamed to %s with overwriting\n", kro.Id, kro.Now)
fmt.Fprintf(w, "Key %s renamed to %s with overwriting\n", kro.Id, cmdenv.EscNonPrint(kro.Now))
} else {
fmt.Fprintf(w, "Key %s renamed to %s\n", kro.Id, kro.Now)
fmt.Fprintf(w, "Key %s renamed to %s\n", kro.Id, cmdenv.EscNonPrint(kro.Now))
}
return nil
}),
Expand Down Expand Up @@ -547,9 +547,9 @@ func keyOutputListEncoders() cmds.EncoderFunc {
tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
for _, s := range list.Keys {
if withID {
fmt.Fprintf(tw, "%s\t%s\t\n", s.Id, s.Name)
fmt.Fprintf(tw, "%s\t%s\t\n", s.Id, cmdenv.EscNonPrint(s.Name))
} else {
fmt.Fprintf(tw, "%s\n", s.Name)
fmt.Fprintf(tw, "%s\n", cmdenv.EscNonPrint(s.Name))
}
}
tw.Flush()
Expand Down
2 changes: 1 addition & 1 deletion core/commands/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func tabularOutput(req *cmds.Request, w io.Writer, out *LsOutput, lastObjectHash
}
}

fmt.Fprintf(tw, s, link.Hash, link.Size, link.Name)
fmt.Fprintf(tw, s, link.Hash, link.Size, cmdenv.EscNonPrint(link.Name))
}
}
tw.Flush()
Expand Down
4 changes: 2 additions & 2 deletions core/commands/mount_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ baz
Type: config.Mounts{},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, mounts *config.Mounts) error {
fmt.Fprintf(w, "IPFS mounted at: %s\n", mounts.IPFS)
fmt.Fprintf(w, "IPNS mounted at: %s\n", mounts.IPNS)
fmt.Fprintf(w, "IPFS mounted at: %s\n", cmdenv.EscNonPrint(mounts.IPFS))
fmt.Fprintf(w, "IPNS mounted at: %s\n", cmdenv.EscNonPrint(mounts.IPNS))

return nil
}),
Expand Down
4 changes: 2 additions & 2 deletions core/commands/name/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ Alternatively, publish an <ipfs-path> using a valid PeerID (as listed by
var err error
quieter, _ := req.Options[quieterOptionName].(bool)
if quieter {
_, err = fmt.Fprintln(w, ie.Name)
_, err = fmt.Fprintln(w, cmdenv.EscNonPrint(ie.Name))
} else {
_, err = fmt.Fprintf(w, "Published to %s: %s\n", ie.Name, ie.Value)
_, err = fmt.Fprintf(w, "Published to %s: %s\n", cmdenv.EscNonPrint(ie.Name), cmdenv.EscNonPrint(ie.Value))
}
return err
}),
Expand Down
2 changes: 1 addition & 1 deletion core/commands/object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ multihash.
fmt.Fprintln(tw, "Hash\tSize\tName")
}
for _, link := range out.Links {
fmt.Fprintf(tw, "%s\t%v\t%s\n", link.Hash, link.Size, link.Name)
fmt.Fprintf(tw, "%s\t%v\t%s\n", link.Hash, link.Size, cmdenv.EscNonPrint(link.Name))
}
tw.Flush()

Expand Down
2 changes: 1 addition & 1 deletion core/commands/pin/remotepin.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Returns a list of objects that are pinned to a remote pinning service.
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RemotePinOutput) error {
// pin remote ls produces a flat output similar to legacy pin ls
fmt.Fprintf(w, "%s\t%s\t%s\n", out.Cid, out.Status, out.Name)
fmt.Fprintf(w, "%s\t%s\t%s\n", out.Cid, out.Status, cmdenv.EscNonPrint(out.Name))
return nil
}),
},
Expand Down
2 changes: 1 addition & 1 deletion core/commands/pubsub.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ To use, the daemon must be run with '--enable-pubsub-experiment'.

func stringListEncoder(req *cmds.Request, w io.Writer, list *stringList) error {
for _, str := range list.Strings {
_, err := fmt.Fprintf(w, "%s\n", str)
_, err := fmt.Fprintf(w, "%s\n", cmdenv.EscNonPrint(str))
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions core/commands/unixfs/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,12 @@ If possible, please use 'ipfs ls' instead.
if len(out.Arguments) > 1 {
for _, arg := range directories[i:] {
if out.Arguments[arg] == hash {
fmt.Fprintf(tw, "%s:\n", arg)
fmt.Fprintf(tw, "%s:\n", cmdenv.EscNonPrint(arg))
}
}
}
for _, link := range object.Links {
fmt.Fprintf(tw, "%s\n", link.Name)
fmt.Fprintf(tw, "%s\n", cmdenv.EscNonPrint(link.Name))
}
}
tw.Flush()
Expand Down
Loading

0 comments on commit fb0a9ac

Please sign in to comment.