From 12554795d2e8b50a4e98f3e7b5024d2a9644ac92 Mon Sep 17 00:00:00 2001 From: Marcel Schramm Date: Thu, 17 Aug 2023 02:03:44 +0200 Subject: [PATCH] fix: arithmetic virus isn't applied to expressions involing string literals anymore (#18) arithmetic virus isn't applied to strings anymore Note that this is only a partial fix (https://github.com/gtramontina/ooze/issues/17), as it only works if the binary expression involves a string literal. Two string variables will still cause an unwanted mutation. --- viruses/arithmetic/arithmetic.go | 33 ++++++++++++++++++++++ viruses/arithmetic/arithmetic_test.go | 3 +- viruses/arithmetic/testdata/source.7.go | 37 +++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 viruses/arithmetic/testdata/source.7.go diff --git a/viruses/arithmetic/arithmetic.go b/viruses/arithmetic/arithmetic.go index a53c792..13aca86 100644 --- a/viruses/arithmetic/arithmetic.go +++ b/viruses/arithmetic/arithmetic.go @@ -33,6 +33,12 @@ func (v *Arithmetic) Incubate(node ast.Node, _ *types.Info) []*viruses.Infection return nil } + // Go overloaded the + operator, meaning is not just used for + // arithmetic operations, so it needs to be ignored for strings. + if isString(expression.X) || isString(expression.Y) { + return nil + } + originalOperation := expression.Op mutatedOperation, matches := v.mutations[expression.Op] @@ -48,3 +54,30 @@ func (v *Arithmetic) Incubate(node ast.Node, _ *types.Info) []*viruses.Infection ), } } + +func isString(expr ast.Expr) bool { + switch expr := expr.(type) { + case *ast.Ident: + // We need to get the type here, however, the `types` parameter + // is explicitly set to `nil` upon function invocation. + case *ast.BasicLit: + // Strings only support concatenation (+) + if expr.Kind == token.STRING { + return true + } + case *ast.CallExpr: + if expr, isIdentifier := expr.Fun.(*ast.Ident); isIdentifier { + // Make sure we have a builtin. + // Technically, we could encounter something like this: + // + // func string(b []byte) int { + // return len(b) + // } + if expr.Name == "string" && expr.Obj == nil { + return true + } + } + } + + return false +} diff --git a/viruses/arithmetic/arithmetic_test.go b/viruses/arithmetic/arithmetic_test.go index ae8ec84..ccdb009 100644 --- a/viruses/arithmetic/arithmetic_test.go +++ b/viruses/arithmetic/arithmetic_test.go @@ -12,7 +12,8 @@ func TestArithmetic(t *testing.T) { "Arithmetic", arithmetic.New(), oozetesting.Mutations{ - "no mutations": {"source.0.go", []string{}}, + "no mutations": {"source.0.go", []string{}}, + "no mutation of string concatenation": {"source.7.go", []string{}}, "one mutation + to -": {"source.1.go", []string{ "source.2.go", }}, diff --git a/viruses/arithmetic/testdata/source.7.go b/viruses/arithmetic/testdata/source.7.go new file mode 100644 index 0000000..fd2bbd6 --- /dev/null +++ b/viruses/arithmetic/testdata/source.7.go @@ -0,0 +1,37 @@ +//!go:build testdata + +package source + +func hello0() string { + x := "hello" + return "/" + x +} + +func hello1() string { + x := "hello" + return x + "/" +} + +func hello3() string { + x := "hello" + y := []byte("/") + return x + string(y) +} + +// FIXME This still fails +// func hello2() string { +// x := "hello" +// y := "/" +// return x + y +// } + +// FIXME This still fails +// func toString(b []byte) string { +// return string(x) +// } + +// func hello4() string { +// x := "hello" +// y := []byte{"/"} +// return hello + toString(y) +// }