Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .regal/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rules:
level: error
exclude-args:
- cfg
- metadata
- rule
style:
line-length:
Expand Down
27 changes: 6 additions & 21 deletions bundle/regal/ast/ast.rego
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ rule_index_strings := [sprintf("%d", [i]) | some i, _ in _rules]
# keyed by rule index
function_calls[rule_index] contains call if {
some rule_index in rule_index_strings
some ref in found.refs[rule_index]
some ref in found.calls[rule_index]

name := ref_to_string(ref[0].value)
args := [arg |
Expand Down Expand Up @@ -344,21 +344,6 @@ all_functions := object.union(config.capabilities.builtins, function_decls(input
# scope of the input AST
all_function_names := object.keys(all_functions)

# METADATA
# description: set containing all negated expressions in input AST
negated_expressions[rule_index] contains value if {
some i, rule in _rules

# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed
rule_index := rule_index_strings[i]

some node in ["head", "body", "else"]

walk(rule[node], [_, value])

value.negated == true
}

# METADATA
# description: |
# true if rule head contains no identifier, but is a chained rule body immediately following the previous one:
Expand Down Expand Up @@ -398,15 +383,15 @@ var_in_call(calls, rule_index, name) if has_named_var(calls[rule_index][_].args[

# METADATA
# description: answers wether provided expression is an assignment (using `:=`)
is_assignment(expr) if {
expr.terms[0].type == "ref"
expr.terms[0].value[0].type == "var"
expr.terms[0].value[0].value == "assign"
is_assignment(term) if {
term.type == "ref"
term.value[0].type == "var"
term.value[0].value == "assign"
}

# METADATA
# description: returns the terms in an assignment (`:=`) expression, or undefined if not assignment
assignment_terms(expr) := [expr.terms[1], expr.terms[2]] if is_assignment(expr)
assignment_terms(terms) := [terms[1], terms[2]] if is_assignment(terms[0])

# METADATA
# description: |
Expand Down
156 changes: 0 additions & 156 deletions bundle/regal/ast/ast_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -4,89 +4,6 @@ import data.regal.ast
import data.regal.capabilities
import data.regal.config

test_find_vars if {
policy := `
package p

global := "foo"

allow if {
a := global
b := [c | c := input[d]]

every e in input {
e == "foo"
}

every f, g in input.bar {
f == g
}

some h, i
input.bar[h][i]
some j in input
some k, l in input

[m, n, o] := [1, 2, 3]

[p, [q, _]] := [1, [2, 1]]

some _, [r, s] in [["foo", "bar"], [1, 2]]

{"x": t} := {"x": 1}

some [u] in [[1]]
}
`

vars := ast.find_vars(regal.parse_module("p.rego", policy).rules) with config.capabilities as capabilities.provided
names := {var.value |
some var in vars
var.type == "var"
}

names == {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u"}
}

test_find_vars_comprehension_lhs if {
policy := `
package p

allow if {
a := [b | input[b]]
c := {d | input[d]}
e := {f: g | g := input[f]}
}
`

vars := ast.find_vars(regal.parse_module("p.rego", policy).rules) with config.capabilities as capabilities.provided
names := {var.value |
some var in vars
var.type == "var"
}

names == {"a", "b", "c", "d", "e", "f", "g"}
}

test_find_vars_function_ret_return_args if {
policy := `
package p

allow if {
walk(input, [path, value])
}
`

module := regal.parse_module("p.rego", policy)
vars := ast.find_vars(module.rules) with config.capabilities as capabilities.provided with input.rules as []
names := {var.value |
some var in vars
var.type == "var"
}

names == {"path", "value"}
}

# https://github.com/StyraInc/regal/issues/168
test_function_decls_multiple_same_name if {
policy := `package p
Expand Down Expand Up @@ -132,62 +49,6 @@ allow := true
]
}

test_find_vars_in_local_scope if {
policy := `
package p

global := "foo"

allow if {
a := global
b := [c | c := input[d]]

every e in input {
f == "foo"
g := "bar"
h == "foo"
}
}`

module := regal.parse_module("p.rego", policy)

allow_rule := module.rules[1]

var_locations := {
"a": {"col": 3, "row": 9},
"b": {"col": 3, "row": 10},
"c": {"col": 13, "row": 10},
"d": {"col": 9, "row": 12},
"e": {"col": 4, "row": 14},
}

var_names(ast.find_vars_in_local_scope(allow_rule, var_locations.a)) with input as module == set()
var_names(ast.find_vars_in_local_scope(allow_rule, var_locations.b)) with input as module == {"a"}
var_names(ast.find_vars_in_local_scope(allow_rule, var_locations.c)) with input as module == {"a", "b", "c"}
var_names(ast.find_vars_in_local_scope(allow_rule, var_locations.d)) with input as module == {"a", "b", "c", "d"}
var_names(ast.find_vars_in_local_scope(allow_rule, var_locations.e)) with input as module == {"a", "b", "c", "d", "e"}
}

test_find_vars_in_local_scope_complex_comprehension_term if {
policy := `
package p

allow if {
a := [{"b": b} | c := input[b]]
}`

module := regal.parse_module("p.rego", policy)

allow_rule := module.rules[0]

ast.find_vars_in_local_scope(allow_rule, {"col": 10, "row": 10}) with input as module == [
{"location": {"col": 3, "row": 7, "text": "YQ=="}, "type": "var", "value": "a"},
{"location": {"col": 15, "row": 7, "text": "Yg=="}, "type": "var", "value": "b"},
{"location": {"col": 20, "row": 7, "text": "Yw=="}, "type": "var", "value": "c"},
{"location": {"col": 31, "row": 7, "text": "Yg=="}, "type": "var", "value": "b"},
]
}

test_find_names_in_scope if {
policy := `
package p
Expand Down Expand Up @@ -218,23 +79,6 @@ test_find_names_in_scope if {
in_scope == {"bar", "global", "comp", "allow", "a", "b", "c", "d", "e"}
}

test_find_some_decl_names_in_scope if {
policy := `package p

allow if {
foo := 1
some x
input[x]
some y, z
input[y][z] == x
}`

module := regal.parse_module("p.rego", policy)

{"x"} == ast.find_some_decl_names_in_scope(module.rules[0], {"col": 1, "row": 6}) with input as module
{"x", "y", "z"} == ast.find_some_decl_names_in_scope(module.rules[0], {"col": 1, "row": 8}) with input as module
}

var_names(vars) := {var.value | some var in vars}

test_provided_capabilities_never_undefined if capabilities.provided == {} with data.internal as {}
Expand Down
66 changes: 33 additions & 33 deletions bundle/regal/ast/search.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,13 @@ _find_nested_vars(obj) := [value |

# simple assignment, i.e. `x := 100` returns `x`
# always returns a single var, but wrapped in an
# array for consistency
_find_assign_vars(value) := var if {
value[1].type == "var"
var := [value[1]]
}

# array for consistency or
# 'destructuring' array assignment, i.e.
# [a, b, c] := [1, 2, 3]
# or
# {a: b} := {"foo": "bar"}
_find_assign_vars(value) := vars if {
value[1].type in {"array", "object"}
vars := _find_nested_vars(value[1])
# [a, b, c] := [1, 2, 3] or {a: b} := {"foo": "bar"}
_find_assign_vars(value) := [value] if {
value.type == "var"
} else := _find_nested_vars(value) if {
value.type in {"array", "object"}
}

# var declared via `some`, i.e. `some x` or `some x, y`
Expand All @@ -32,16 +26,10 @@ _find_some_decl_vars(value) := [v |
]

# single var declared via `some in`, i.e. `some x in y`
_find_some_in_decl_vars(value) := vars if {
arr := value[0].value
count(arr) == 3

vars := _find_nested_vars(arr[1])
}
_find_some_in_decl_vars(arr) := _find_nested_vars(arr[1]) if count(arr) == 3

# two vars declared via `some in`, i.e. `some x, y in z`
_find_some_in_decl_vars(value) := vars if {
arr := value[0].value
_find_some_in_decl_vars(arr) := vars if {
count(arr) == 4

vars := [v |
Expand Down Expand Up @@ -128,14 +116,14 @@ _find_vars(value, last) := {"term": find_term_vars(function_ret_args(fn_name, va
# left-hand side is equally dubious, but we'll treat `x = 1` as `x := 1` for
# the purpose of this function until we have a more robust way of dealing with
# unification
_find_vars(value, last) := {"assign": _find_assign_vars(value)} if {
_find_vars(value, last) := {"assign": _find_assign_vars(value[1])} if {
last == "terms"
value[0].type == "ref"
value[0].value[0].type == "var"
value[0].value[0].value in {"assign", "eq"}
}

_find_vars(value, last) := {"somein": _find_some_in_decl_vars(value)} if {
_find_vars(value, last) := {"somein": _find_some_in_decl_vars(value[0].value)} if {
last == "symbols"
value[0].type == "call"
}
Expand Down Expand Up @@ -208,10 +196,13 @@ _rules := data.workspace.parsed[input.regal.file.uri].rules if not input.rules
# - somein
# - ref
found.vars[rule_index][context] contains var if {
some i, rule_index in rule_index_strings
some i, rule in _rules

rule_index := rule_index_strings[i]

some node in ["head", "body", "else"]

walk(_rules[i][node], [path, value])
walk(rule[node], [path, value])

last := regal.last(path)
last in {"terms", "symbols", "args"}
Expand All @@ -233,7 +224,6 @@ found.vars[rule_index].ref contains var if {

# METADATA
# description: all refs found in module
# scope: document
found.refs[rule_index] contains value if {
some i, rule in _rules

Expand All @@ -245,7 +235,9 @@ found.refs[rule_index] contains value if {
value.type == "ref"
}

found.refs[rule_index] contains value if {
# METADATA
# description: all calls found in module
found.calls[rule_index] contains value if {
some i, rule in _rules

# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed
Expand Down Expand Up @@ -280,6 +272,21 @@ found.comprehensions[rule_index] contains value if {
value.type in {"arraycomprehension", "objectcomprehension", "setcomprehension"}
}

# METADATA
# description: set containing all negated expressions in input AST
found.expressions[rule_index] contains value if {
some i, rule in _rules

# converting to string until https://github.com/open-policy-agent/opa/issues/6736 is fixed
rule_index := rule_index_strings[i]

some node in ["head", "body", "else"]

walk(rule[node], [_, value])

value.terms
}

# METADATA
# description: |
# finds all vars declared in `rule` *before* the `location` provided
Expand Down Expand Up @@ -363,10 +370,3 @@ find_some_decl_names_in_scope(rule, location) := {some_var.value |
some some_var in found.vars[_rule_index(rule)]["some"]
_before_location(rule.head, some_var, location)
}

# METADATA
# description: all expressions in module
exprs[rule_index][expr_index] := expr if {
some rule_index, rule in input.rules
some expr_index, expr in rule.body
}
Loading
Loading