Replies: 3 comments 1 reply
-
Package example You want to know if xorg packages are installed on the system. Actual MQL: packages.where( name == /^xserver-xorg.*/ ).length == 0 New MQL style: packages.where( name == /^xserver-xorg.*/ ).assert ( length == 0 ) [failed] Expected at most 0 packages, found 2.
[
0: {
name: "xserver-xorg-driver",
version: 1.1.0
... iptables example you want to check if there are two iptables rules. old style: iptables.input.length == 2 new style: iptables.input.assert( length == 2 ) [failed] Expected at 2 iptables, found 4.
[
0: {
source: "0.0.0.0/0",
destination: "0.0.0.0/0",
options: "udp dpt:323 state NEW",
chain: "input",
target: "ACCEPT"
... |
Beta Was this translation helpful? Give feedback.
-
@arlimus First of all, thank you for the deep dive into this problem. I really like a lot of the built-in function ideas you have above, and would expect those to be useful to some users. I am looking at this through the lens of making results clear and actionable for users. I expect that the person responding to a failed check will likely not be the person that wrote the check. No matter if the check results are rendered in Mondoo Platform, or in I believe |
Beta Was this translation helpful? Give feedback.
-
@arlimus Thanks for the detailed write-up and fresh set of ideas. Regarding 1. Use the
|
Beta Was this translation helpful? Give feedback.
-
In MQL we sometimes want to say that the number of entries should be within a certain range, e.g. There should be between 1 and 3 root users (we know at least 1 will be there but if there are more than 3 then we have a problem).
[failed] 4 is not <= 3
The problem: we don’t get the list of users returned. Ideally we would want to know, if there are too many users, who they are. As soon as this check fails, we would want to to know more. The only thing that the check returns, however, is the number of entries found. This is fundamentally correct (and we don't want to change that), but we do want to offer a way to solve this problem.
Discussion
There are a few ways to tackle this problem:
1. Use the
@msg
tagWhile originally meant as a workaround only, @scottford-io reminded me today that it’s a valid approach for all cases where the context cannot be expressed in the query alone. As much as we want default queries to output the right thing, there will be cases where additional contextual information is helpful. The
@msg
tag is ideal for those cases.How well does it serve this use-case?
Applied to the problem above, we get:
There should be no more than 3 users, however we detected 4 users: [ 0: { name: "root", uid: 0 ...
Tons of flexibility, however it also requires a lot of writing, the context only really lives in the message, and we still need to repeat a part of the query. This can be mitigated with variables:
As an upside, we get the flexibility to do anything we want. It also allows us to attach messages to any of the assertions in a chain of checks (though note: those should probably be written separately; but that's for a different day...)
Let's call this approach the fallback: It is always there for us.
2. Introduce a new check for list entries
For example, we could do something like:
[failed] Expected users to contain at most 3 entries, but found 4 instead: [ 0: { name: "root", uid: 0 ...
This has all the benefits we get from other list assertions: print more or less entries depending on the printer config, get a nice message out of the box, and continue to work with the data (e.g. subfield extraction down the line). However, it comes at the cost of a very specific keyword that only applies to list (and maps + dicts).
The list of possible keywords are:
"Within range" could replace "at most" and "at least", to reduce the keyword pressure. However, it would mandate "outsie of range" as well.
Alternatively we could scrap both "outside of range" and "within range" in favor of "at most" and "at least" and ensure that the context extraction works inside of nested boolean statements (which it should anyway).
One more semantic downside is that this can be misunderstood if you deal with a list of numbers. In that case e.g.
[1,4,8,9].containsAtMost(5)
can easily be misunderstood as meaning the number in the list, instead of the number of entries. Better wording suggestions are welcome (looked across a few languages and didn't find anything good yet).3. Introduce
assert
This is a more far-reaching approach to this problem, which isn't limited to checking list entry numbers:
The
assert
keyword creates a way to write assertions for an object without focusing on the value that is being tested. When the assertion fails, we retrieve the entire object and return it. The function itself is boolean, but it returns secondary values much in the same wayall
andnone
work for lists. The only difference is that it's not limited to lists: it works on any resource.For fun, translating between them:
The downside is that a lot of the added context is hidden within the round brackets. We'd want to extend the way we inspect the checks to print better messages in a follow-up:
Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions