diff --git a/gremlin-go/driver/gremlinlang.go b/gremlin-go/driver/gremlinlang.go index baec7e65471..2983fbc559d 100644 --- a/gremlin-go/driver/gremlinlang.go +++ b/gremlin-go/driver/gremlinlang.go @@ -110,16 +110,40 @@ func (gl *GremlinLang) addToGremlin(name string, args ...interface{}) error { return nil } +// escapeString escapes a string value for safe embedding in a GremlinLang script. +func escapeString(s string) string { + var sb strings.Builder + for _, c := range s { + switch c { + case '\\': + sb.WriteString(`\\`) + case '"': + sb.WriteString(`\"`) + case '\n': + sb.WriteString(`\n`) + case '\r': + sb.WriteString(`\r`) + case '\t': + sb.WriteString(`\t`) + case '\b': + sb.WriteString(`\b`) + case '\f': + sb.WriteString(`\f`) + default: + sb.WriteRune(c) + } + } + return sb.String() +} + func (gl *GremlinLang) argAsString(arg interface{}) (string, error) { if arg == nil { return "null", nil } - // we are concerned with both single and double quotes and %q in fmt only escapes double quotes - escapeQuotes := strings.NewReplacer(`'`, `\'`, `"`, `\"`) switch v := arg.(type) { case string: - return fmt.Sprintf("\"%s\"", escapeQuotes.Replace(v)), nil + return fmt.Sprintf("\"%s\"", escapeString(v)), nil case bool: return strconv.FormatBool(v), nil case int8, uint8: diff --git a/gremlin-go/driver/gremlinlang_test.go b/gremlin-go/driver/gremlinlang_test.go index 7160056f242..e9cb5e6d1cb 100644 --- a/gremlin-go/driver/gremlinlang_test.go +++ b/gremlin-go/driver/gremlinlang_test.go @@ -674,6 +674,12 @@ func Test_GremlinLang(t *testing.T) { }, equals: "g.inject(NaN).is(eq(NaN))", }, + { + assert: func(g *GraphTraversalSource) *GraphTraversal { + return g.V().Has("name", "\"marko\n\r\t\b\f\"") + }, + equals: "g.V().has(\"name\",\"\\\"marko\\n\\r\\t\\b\\f\\\"\")", + }, } var testsToRun []test