Parameters in Rego rules [Open Policy Agent] - open-policy-agent

How to use parameters in Rego rules? I would have something like this:
deny[reason] {
input.request.kind.kind == "Route"
not valid_route_request[label]
reason := sprintf("missing or wrong router selector label: %v", [label])
}
valid_route_request[label] {
requester := input.request.userInfo.username
some i # iterate on all users
requester == data.kubernetes.users[i].metadata.name
label := input.request.object.metadata.labels["router-selector"]
label == data.kubernetes.users[i].metadata.annotations[router_selector_key]
}
where label is used to build the error message. I get error from OPA: var label is unsafe ...
Generally speaking, it is still not clear to me how to pass parameters in Rego.

Your example is almost correct--the problem you're facing is that label is "unsafe".
TLDR; assign label inside the deny rule:
deny[reason] {
input.request.kind.kind == "Route"
label := input.request.object.metadata.labels["router-selector"]
not valid_route_request[label]
reason := sprintf("wrong 'router-selector' label: %v", [label])
}
deny[reason] {
input.request.kind.kind == "Route"
not input.request.object.metadata.labels["router-selector"]
reason := "missing 'router-selector' label"
}
Note, I've created TWO deny rules. One for the case where the path input.request.object.metadata.labels["route-selector'] is undefined and the other for an invalid value.
Why does OPA generate a safety error in the original example? Safety is a property of Rego that ensures that all variables can be assigned a finite number of values. To be considered "safe", a variable must appear as the output of at-least-one non-negated expression.
Here are some examples that are all safe:
# 'a' is assigned to the value referenced by input.foo
a := input.foo
# 'x' is assigned to the keys of the collection referenced by input.foo
input.foo[x]
# 'label' is is assigned to the keys of the collection referenced by valid_route_request
valid_route_request[label]
# 'x' is safe because it is assigned outside the negated expression
x := 7; not illegal_numbers[x]
Here are examples of unsafe expressions:
# 'x' is unsafe because it does not appear as an output of a non-negated expression
not p[x]; not q[x]
# 'y' is unsafe because it only appears as a built-in function input
count(y)
Safety errors can also occur with variables that appear in the head of the rule:
# 'msg' is unsafe because it is not assigned inside the body of the rule.
deny[msg] {
input.request.kind.kind == "BadKind"
}
Safety is important as it ensures that OPA can enumerate all of the values that could be assigned to the variable. If the variable is unsafe it means there could be an infinite number of variable assignments. In your example, the statement valid_route_request generates a set of values (labels?). The not valid_route_request[label] statement in the deny rule is unsafe because label is not assigned elsewhere in the deny rule (and label does not appear in the global scope presumably.) This actually becomes a bit clearer if you include 'some' in the deny rule:
deny[reason] {
some label
input.request.kind.kind == "Route"
not valid_route_request[label]
reason := ...
}
This rule says (in English):
reason is in deny if for some label, input.request.kind.kind equals Route and label is not in valid_route_request, and ...
Technically there would be an infinite number of assignments to label that satisfy this rule (e.g., the string "12345" would NOT be contained in valid_route_request and so would "123456" and so would ...)

Related

Why is Dafny allowing uninitialized return result?

In this method:
datatype Results = Foo | Bar
method test() returns (r:Result)
{
}
Dafny verifies OK and test() returns Foo. Which is technically correct (it does return a value of the correct type) however I was expecting Dafny to complain that the result has not been set by the method itself. What test() is doing is similar to doing:
return;
in a C function that is supposed to return an int.
Is there a way to make Dafny verify that a methods results are always set before the method returns?
The flag you want is /definiteAssignment:2:
/definiteAssignment:<n>
0 - ignores definite-assignment rules; this mode is for testing only--it is
not sound
1 (default) - enforces definite-assignment rules for compiled variables and fields
whose types do not support auto-initialization and for ghost variables
and fields whose type is possibly empty
2 - enforces definite-assignment for all non-yield-parameter
variables and fields, regardless of their types
3 - like 2, but also performs checks in the compiler that no nondeterministic
statements are used; thus, a program that passes at this level 3 is one
that the language guarantees that values seen during execution will be
the same in every run of the program
This is what Dafny says on your code says with that flag:
test.dfy(5,0): Error: out-parameter 'r', which is subject to definite-assignment rules, might be uninitialized at this return point

Dart: How to check if List of Objects is a List of Strings/ints/bools

For simple data types, you can use e.g.
object is String
to check whether an Object variable is of a more specific type.
But let's you have a List, but want to check if it is a List of Strings. Intuitively we might try
List list = ['string', 'other string'];
print(list is List<String>);
which returns false.
Similarly using the List.cast() method doesn't help, it will always succeed and only throw an error later on when using the list.
We could iterate over the entire list and check the type of each individual entry, but I was hoping there might be a better way.
There is no other way. What you have is a List<Object?>/List<dynamic> (because the type is inferred from the variable type, which is a raw List type which gets instantiated to its bound). The list currently only contains String objects, but nothing prevents you from adding a new Object() to it.
So, the object itself doesn't know that it only contains strings, you have to look at each element to check that.
Or, when you create a list, just declare the variable as List<String> list = ...; or var list = ...;, then the object will be a List<String>.
If you are not the one creating the list, it's back to list.every((e) => e is String).
Each element of a List may be of any type BUT IF YOU ARE SURE that all elements have the same type this approach may be useful
bool listElementIs<T>(List l) {
if (l.isEmpty) return true;
try {
if (l[0] is T) return true; // only try to access to check element
} catch (e) {
return false;
}
return false;
}
void main() {
List list = ['string', 'other string'];
print(listElementIs<String>(list)); // prints 'true'
print(listElementIs<int>(list)); // prints 'false'
}
I think a better way to do this is to be specific about your data types.
By specifying the types of variables, you can catch potential errors early on during the development process.
In addition, specifying the types of variables makes the code more readable and understandable.
Furthermore, specifying types can also improve the performance of your code, as the compiler can make certain optimizations based on the types of variables.
List<String> list = <String>['string', 'other string'];
print(list is List<String>); /// prints true.
You can use this linter rule to enforce it:
https://dart-lang.github.io/linter/lints/always_specify_types.html

How does null assertion work in dart and when can I use it?

Can someone simply explain to me how null assertion (!) works and when to use it?
The ! operator can be used after any expression, e!.
That evaluates the expression e to value v, then checks whether v is the null value. If it is null, an error is thrown. If not, then e! also evaluates to v.
The static type of an expression e! is (basically) the static type of e with any trailing ?s remove. So, if e has type int?, the type of e! is int.
You should not use e! unless e can be null (the type of e is potentially nullable).
The ! operator is dynamically checked. It can throw at runtime, and there is no static check which can guarantee that it won't. It's like using a value with type dynamic in that all the responsibility of preventing it from throwing is on the author, the compiler can't help you, and you need good tests to ensure that it won't throw when it's not supposed to.
It's called an assertion because it should never throw in production code.
So, use e! when you know (for some reason not obvious to the compiler, perhaps because of some invariant guaranteeing that the value is not null while something else is true) that e is not null.
Example:
abstract class Box<T extends Object> {
bool hasValue;
T? get value;
}
...
Box<int> box = ...;
if (box.hasValue) {
var value = box.value!;
... use value ...
}
If you are repeatedly using ! on the same expression, do consider whether it's more efficient to read it into a local variable just once.
Also, if (like this Box example) the value being null is equivalent to the other test you just did, maybe just check that directly:
Box<int> box = ...;
var value = box.value;
if (value != null) {
... use value ...
}
This code, with an explicit != null check on a local variable, is statically guaranteed to not throw because the value is null.
The code using ! above relies on the author to maintain whichever invariant allowed them to write the !, and if something changes, the code might just start throwing at runtime. You can't tell whether it's safe just by looking at the code locally.
Use ! sparingly, just like the dynamic type and late declarations, because they're ways to side-step the compiler's static checking and ensure it that "this is going to be fine". That's a great feature when you need it, but it's a risk if you use it unnecessarily.

Dart const class argument checking

How does one perform argument checking on const class?
For example:
class Data {
String value;
const Data(this.value);
}
How can I check that for example value.contains("banana") == true?
If I try to check it in assert like below, linter reports error Invalid constant value. dart(invalid_constant)
class Data {
String value;
const Data(this.value)
: assert(value.contains("banana");
}
class Data {
String value;
const Data(this.value)
: assert(value.contains("banana");
}
Well for one, you are missing a parenthesis after your assertion. For another, in a constant class, all the fields must be marked as final.
But the last thing (which is what is actually relevant to your question) is that if your constructor is marked as const, then all values and operations in your assertions have to be constants. This is because constant instances are initialized at compile-time and the compiler can't perform assertions where it has to run code in order to validate the constructor data.
This means you can perform things like equality or other boolean operator checks against other constant values:
: assert(value == 'banana');
But you can't compare against non-constant values or do things like call methods (note: the shown errors are not the errors that the compiler will actually report):
: assert(value == Foo()); // Error: Foo is not a constant
: assert(value.contains('banana')); // Error: method calls are not a constant operation
: assert(value == const Bar()); // Error: Bar is not a compiler-known constant
That last error may be a little obtuse seeing as Bar is a constant class object. The reason it doesn't work, though, is because in order to implement == (the boolean equals operator), Bar has to define code for that operator, and the compiler can't run code in constant assertions. The end result is that, even if an object is marked as a constant, it still can't be used in an assertion. You can only use primitive constants, i.e. Null, bool, int, double, String.

Overwriting array in dafny

I'm trying to overwrite an array inside a method. The compiler is giving me the error "Error: LHS of assignment must denote a mutable variable".
method invalidSort(a : array<int>)
modifies a;
requires a != null;
ensures sorted(a[..]);
{
a := new int[0];
}
Am I staring myself blind and missing something or why does Dafny not allow this?
In Dafny, method parameters cannot be assigned to. You can use a local variable if you need to update the value internally.
For example,
var a' := new int[0];
If you want this new array to be available to the caller, you'll also need to return it.
return a';
All that said, if you're trying to write an in-place sorting method, then you don't need to do any of this. Just modify a in place.
a[0] := 0;
// ...

Resources