Title
#help
m

Matthew Ebeweber

09/29/2022, 5:54 PM
QQ, I'm seeing different results when I change a rule from the following to the other (both should be booleans). Are these not functionally equivalent ?
match:
  any:
    of:
      - expr: V.foo
      - expr: V.bar
vs.
match:
  expr: V.foo || V.bar
Charith (Cerbos)

Charith (Cerbos)

09/29/2022, 6:12 PM
They should be equivalent. The caveat is that the
||
operator does not short-circuit whereas
anyOf
does. I don't see how that would affect the result of these expressions though. What are the definitions of
V.bar
and
V.foo
?
m

Matthew Ebeweber

09/29/2022, 6:42 PM
isScoped: P.attr.scopedTenantQid != ''
  isUnscoped: |-
    P.attr.scopedTenantQid == ''
  isScopedToCorpTenant: |-
    P.attr.isCorpTenant && P.attr.tenantQid == P.attr.scopedTenantQid
They look like this where isCorpTenant is a boolean and fooQid variables are a string (empty or with a value).
Charith (Cerbos)

Charith (Cerbos)

09/30/2022, 8:45 AM
I have been digging into this a bit. If one of the expressions in an
any
block fails to evaluate or returns a non-boolean value, we stop evaluating the rest of the expressions in the block and return
false
immediately. I think that's probably what you have seen. It's likely that
V.foo
returns a non-boolean value because one of the attributes used to calculate it returns a
null
. I ran a test with a principal that didn't have the
scopedTenantQid
attribute and got the following trace:
policy=cerbos.resource.TEST.vdefault > scope="" > variables > isUnscoped=`P.attr.scopedTenantQid == ''`
    activated
    result → null

  policy=cerbos.resource.TEST.vdefault > scope="" > variables > isUnscoped=`P.attr.scopedTenantQid == ''`
    activated
    result → null

  policy=cerbos.resource.TEST.vdefault > scope="" > variables > isUnscoped=`P.attr.scopedTenantQid == ''`
    activated
    result → null

  policy=cerbos.resource.TEST.vdefault > scope="" > rule=rule-002 > action=B > condition > conditionAny > condition#0 > expr=`V.isScoped`
    activated
    result → false
    Failed to evaluate expression: unexpected result: wanted bool, got structpb.NullValue

  policy=cerbos.resource.TEST.vdefault > scope="" > rule=rule-002 > action=B > condition > conditionAny
    activated
    result → false
    Short-circuited: failed to evaluate `V.isScoped`: unexpected result: wanted bool, got structpb.NullValue

  policy=cerbos.resource.TEST.vdefault > scope="" > rule=rule-002 > action=B
    skipped
    Error evaluating condition: failed to evaluate `V.isScoped`: unexpected result: wanted bool, got structpb.NullValue
If
scopedTenantQid
doesn't exist,
scopedTenantQid == ''
evaluates to
null
and it's not a boolean, so Cerbos gives up checking the rest because it thinks there's an error in the expression and it's safer to fail than carry on.
8:51 AM
I think we can relax this in the engine and implicitly treat non-boolean values as
false
values. But, there might be other unintended consequences of that so it needs a bit more pondering. In the mean time, using schemas to enforce required attributes could help catch problems like this early.
m

Matthew Ebeweber

09/30/2022, 2:05 PM
Gotcha! I'm not sure there's any need to relax the behavior. Sounds a bit dangerous.
2:14 PM
That said, do these unexpected evaluations end up in the logs? I'd like to squash them where possible. Additionally, if I want to handle the absence of an attribute or introduce a new one. What's the best way to do that safely? Say I want to add
P.attr.foo
. If
foo
isn't present I want to the system to ignore that rule for now (avoid updating every test or weird permission issues during a deploy). Adding a
P.attr.foo == null
doesn't seem to do the trick.
2:18 PM
Eh, to answer my own question, I guess I can add it to my principal builder and deploy the policy changes in a followup. It's all in a mono-repo so mainly trying to avoid the double PR + updating all existing tests.
Charith (Cerbos)

Charith (Cerbos)

09/30/2022, 2:18 PM
You can do
has(P.attr.foo)
to check whether
foo
exists
2:20 PM
We have a REPL where you can try out CEL expressions https://docs.cerbos.dev/cerbos/latest/cli/cerbos.html#repl
-> :let x = {"y": "z"}
x = {
  "y": "z"
}

-> has(x.x)
_ = false

-> has(x.y)
_ = true
m

Matthew Ebeweber

09/30/2022, 2:21 PM
TIL there's more definitions in the CEL spec