mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
fix: preserve all bindings for partial object iteration
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/34a4e1b3-d364-46c4-9998-b00780f5d339 Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
8617c79da5
commit
117671d959
@@ -1797,7 +1797,10 @@ impl Interpreter {
|
||||
comps.pop();
|
||||
output
|
||||
} else {
|
||||
// Rule's constness is determined only by its ref.
|
||||
// Implicit-true partial object rules can vary with each successful key binding.
|
||||
if key_expr.is_some() && !is_old_style_set {
|
||||
is_const_rule = false;
|
||||
}
|
||||
Value::Bool(true)
|
||||
};
|
||||
|
||||
|
||||
@@ -55,11 +55,8 @@ impl<'a> Compiler<'a> {
|
||||
if let Rule::Spec { head, .. } = def.as_ref() {
|
||||
match head {
|
||||
RuleHead::Set { .. } => RuleType::PartialSet,
|
||||
RuleHead::Compr { refr, assign, .. } => match refr.as_ref() {
|
||||
crate::ast::Expr::RefBrack { .. } if assign.is_some() => {
|
||||
RuleType::PartialObject
|
||||
}
|
||||
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
|
||||
RuleHead::Compr { refr, .. } => match refr.as_ref() {
|
||||
crate::ast::Expr::RefBrack { .. } => RuleType::PartialObject,
|
||||
_ => RuleType::Complete,
|
||||
},
|
||||
_ => RuleType::Complete,
|
||||
|
||||
@@ -454,7 +454,19 @@ impl RegoVM {
|
||||
let mut obj_value = self.take_register(obj)?;
|
||||
|
||||
if let Ok(obj_mut) = obj_value.as_object_mut() {
|
||||
obj_mut.insert(key_value, value_value);
|
||||
match obj_mut.get(&key_value) {
|
||||
Some(existing_value) if existing_value != &value_value => {
|
||||
self.set_register(obj, obj_value)?;
|
||||
return Err(VmError::RuleMultipleOutputs { pc: self.pc });
|
||||
}
|
||||
Some(_) => {
|
||||
self.set_register(obj, obj_value)?;
|
||||
return Ok(InstructionOutcome::Continue);
|
||||
}
|
||||
None => {
|
||||
obj_mut.insert(key_value, value_value);
|
||||
}
|
||||
}
|
||||
self.set_register(obj, obj_value)?;
|
||||
} else {
|
||||
let offending = obj_value.clone();
|
||||
|
||||
@@ -209,6 +209,9 @@ pub enum VmError {
|
||||
#[error("Rule-data conflict: {message} (pc={pc})")]
|
||||
RuleDataConflict { message: String, pc: usize },
|
||||
|
||||
#[error("rules must not produce multiple outputs (pc={pc})")]
|
||||
RuleMultipleOutputs { pc: usize },
|
||||
|
||||
#[error("Arithmetic error: {message} (pc={pc})")]
|
||||
ArithmeticError { message: String, pc: usize },
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ impl RegoVM {
|
||||
| VmError::MemoryLimitExceeded { .. }
|
||||
| VmError::RegexSizeLimitExceeded { .. }
|
||||
| VmError::InstructionLimitExceeded { .. }
|
||||
| VmError::RuleMultipleOutputs { .. }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
201
tests/interpreter/cases/rule/partial_object_iteration.yaml
Normal file
201
tests/interpreter/cases/rule/partial_object_iteration.yaml
Normal file
@@ -0,0 +1,201 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cases:
|
||||
- note: partial_object_iteration_some_in_object_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_some_in_array_v1
|
||||
data: {}
|
||||
input:
|
||||
arr: ["FOO", "BAR", "BAZ"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[v] if {
|
||||
some _, v in input.arr
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_with_filter_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
k != "BAR"
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_input_lookup_future_keywords
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.if
|
||||
|
||||
violations[k] if {
|
||||
input.x[k]
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_multiple_bodies_collect_all_keys_v1
|
||||
data: {}
|
||||
input:
|
||||
primary:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
secondary:
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.primary
|
||||
}
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.secondary
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_duplicate_keys_same_value_are_ok_v1
|
||||
data: {}
|
||||
input:
|
||||
arr: ["FOO", "FOO", "BAR"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[v] if {
|
||||
some _, v in input.arr
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
violations:
|
||||
BAR: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_duplicate_keys_different_values_error_v1
|
||||
data: {}
|
||||
input:
|
||||
entries:
|
||||
- k: "FOO"
|
||||
v: 1
|
||||
- k: "FOO"
|
||||
v: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] := v if {
|
||||
some entry in input.entries
|
||||
k := entry.k
|
||||
v := entry.v
|
||||
}
|
||||
query: data.test.violations
|
||||
error: "rules must not produce multiple outputs"
|
||||
|
||||
- note: partial_object_and_partial_set_iteration_coexist_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
|
||||
seen contains k if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
seen:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: complete_rule_same_value_definitions_still_work_v1
|
||||
data: {}
|
||||
input:
|
||||
role: "superuser"
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
allowed if {
|
||||
input.role == "admin"
|
||||
}
|
||||
|
||||
allowed if {
|
||||
input.role == "superuser"
|
||||
}
|
||||
query: data.test.allowed
|
||||
want_result: true
|
||||
200
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
200
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
@@ -0,0 +1,200 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cases:
|
||||
- note: partial_object_iteration_some_in_object_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_some_in_array_v1
|
||||
data: {}
|
||||
input:
|
||||
arr: ["FOO", "BAR", "BAZ"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[v] if {
|
||||
some _, v in input.arr
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_with_filter_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
k != "BAR"
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_iteration_input_lookup_future_keywords
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.if
|
||||
|
||||
violations[k] if {
|
||||
input.x[k]
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_multiple_bodies_collect_all_keys_v1
|
||||
data: {}
|
||||
input:
|
||||
primary:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
secondary:
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.primary
|
||||
}
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.secondary
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_duplicate_keys_same_value_are_ok_v1
|
||||
data: {}
|
||||
input:
|
||||
arr: ["FOO", "FOO", "BAR"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[v] if {
|
||||
some _, v in input.arr
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_duplicate_keys_different_values_error_v1
|
||||
data: {}
|
||||
input:
|
||||
entries:
|
||||
- k: "FOO"
|
||||
v: 1
|
||||
- k: "FOO"
|
||||
v: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] := v if {
|
||||
some entry in input.entries
|
||||
k := entry.k
|
||||
v := entry.v
|
||||
}
|
||||
query: data.test.violations
|
||||
want_error: "multiple outputs"
|
||||
|
||||
- note: partial_object_and_partial_set_iteration_coexist_v1
|
||||
data: {}
|
||||
input:
|
||||
x:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
|
||||
seen contains k if {
|
||||
some k, _ in input.x
|
||||
}
|
||||
|
||||
main := {
|
||||
"seen": seen,
|
||||
"violations": violations,
|
||||
}
|
||||
query: data.test.main
|
||||
want_result:
|
||||
seen:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
violations:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: complete_rule_same_value_definitions_still_work_v1
|
||||
data: {}
|
||||
input:
|
||||
role: "superuser"
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
allowed if {
|
||||
input.role == "admin"
|
||||
}
|
||||
|
||||
allowed if {
|
||||
input.role == "superuser"
|
||||
}
|
||||
query: data.test.allowed
|
||||
want_result: true
|
||||
Reference in New Issue
Block a user