Files
releases/topdown/json_test.go
T
Philip Conrad b7f957d552 topdown/json: Fix panic in json.filter on empty JSON paths. (#5200)
This commit fixes a panic discovered in the `json.filter` builtin that
could be triggered with an empty JSON path parameter, such as `""`. This
panic was caused by indexing logic in a helper function always assuming
it had at least one path segment to work with, and thus indexing
out-of-bounds when no path segment was present.

The issue was fixed by adding an extra check to the helper function for
the null path case, and adding new unit tests to check for the issue.

Fixes: #5199

Signed-off-by: Philip Conrad <philipaconrad@gmail.com>
2022-09-29 22:55:25 -04:00

114 lines
2.9 KiB
Go

// Copyright 2019 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"testing"
"github.com/open-policy-agent/opa/ast"
)
func TestFiltersToObject(t *testing.T) {
cases := []struct {
note string
filters []string
expected string
}{
{
note: "empty path",
filters: []string{`""`},
expected: `{}`,
},
{
note: "base",
filters: []string{`"a/b/c"`},
expected: `{"a": {"b": {"c": null}}}`,
},
{
note: "root prefixed",
filters: []string{`"a/b/c"`},
expected: `{"a": {"b": {"c": null}}}`,
},
{
note: "trailing slash",
filters: []string{`"a/b/c"`},
expected: `{"a": {"b": {"c": null}}}`,
},
{
note: "different roots",
filters: []string{`"a/b/c"`, `"d/e/f"`},
expected: `{"a": {"b": {"c": null}}, "d": {"e": {"f": null}}}`,
},
{
note: "shared root",
filters: []string{`"a/b/c"`, `"a/b/d"`},
expected: `{"a": {"b": {"c": null, "d": null}}}`,
},
{
note: "multiple shares at different points",
filters: []string{`"a/b/c"`, `"a/b/d"`, `"a/e/f"`},
expected: `{"a": {"b": {"c": null, "d": null}, "e": {"f": null}}}`,
},
{
note: "conflict with one ordering",
filters: []string{`"a"`, `"a/b"`},
expected: `{"a": null}`,
},
{
note: "conflict with reverse ordering",
filters: []string{`"a/b"`, `"a"`},
expected: `{"a": null}`,
},
{
note: "arrays",
filters: []string{`"a/1/c"`, `"a/1/b"`},
expected: `{"a": {"1": {"c": null, "b": null}}}`,
},
{
note: "non string keys",
filters: []string{`[[1], {2}]`, `"a/1/b"`},
expected: `{"a": {"1": {"b": null}}, [1]: {{2}: null}}`,
},
{
note: "escaped tilde",
filters: []string{`"a/~0b~0/c~0"`},
expected: `{"a": {"~b~": {"c~": null}}}`,
},
{
note: "escaped slash",
filters: []string{`"a/~1b~1c/d~1"`},
expected: `{"a": {"/b/c": {"d/": null}}}`,
},
{
note: "mixed escapes",
filters: []string{`"a/~0b~1c/d~1~0"`},
expected: `{"a": {"~b/c": {"d/~": null}}}`,
},
{
note: "empty strings mixed with normal paths",
filters: []string{`"a/b/c"`, `""`, `"a/b/d"`, `"a/e/f"`, `""`},
expected: `{"a": {"b": {"c": null, "d": null}, "e": {"f": null}}}`,
},
}
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
var paths []ast.Ref
for _, path := range tc.filters {
parsedPath, err := parsePath(ast.MustParseTerm(path))
if err != nil {
t.Errorf("unexpected error parsing path %s: %s", path, err)
}
paths = append(paths, parsedPath)
}
actual := pathsToObject(paths)
expected := ast.MustParseTerm(tc.expected)
if actual.Compare(expected.Value) != 0 {
t.Errorf("Unexpected object from filters:\n\nExpected:\n\t%s\n\nActual:\n\t%s\n\n", expected.Value.String(), actual.String())
}
})
}
}