diff --git a/README.md b/README.md index d3eb60d7..6b13810e 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,12 @@ You can build release files (e.g., `aeacus-linux.zip`). These will have auto-ran ## Documentation -All checks (with examples and notes) [are documented here](docs/checks.md). +All check condition types (with examples and notes) [are documented here](docs/checks.md). Other documentation: - [Non-Check Scoring Configuration](docs/config.md) +- [Condition Precedence](docs/conditions.md) +- [Adding Hints](docs/hints.md) - [Crypto](docs/crypto.md) - [Security Model](docs/security.md) - [Remote Reporting](docs/remote.md) diff --git a/checks_linux.go b/checks_linux.go index 73c5db3d..3470a7f7 100644 --- a/checks_linux.go +++ b/checks_linux.go @@ -210,8 +210,9 @@ func (c cond) PermissionIs() (bool, error) { func (c cond) ProgramInstalled() (bool, error) { c.requireArgs("Name") result, err := cond{ - Cmd: "dpkg -s " + c.Name, - }.Command() + Cmd: "dpkg -s " + c.Name, + Value: " install", + }.CommandContains() // If dpkg fails, use rpm if err != nil { diff --git a/docs/conditions.md b/docs/conditions.md new file mode 100644 index 00000000..96597b80 --- /dev/null +++ b/docs/conditions.md @@ -0,0 +1,45 @@ +# Check conditions and precedence + +Using multiple conditions for a check can be confusing at first, but can greatly improve the quality of your images by accounting for edge cases and abuse. + +Given no conditions, a check does not pass. + +If any **Fail** conditions succeed, the check does not pass. + +**PassOverride** conditions act as a logical OR. This means that any can succeed for the check to pass. + +**Pass** conditions act as a logical AND with other pass conditions. This means they must ALL be true for a check to pass. + +If the outcome of a check is decided, aeacus will NOT execute the remaining conditions (it will "short circuit"). For example, if a PassOverride succeeds, any Pass conditions are NOT executed. + +So, it's like this: `check_passes = (NOT fails) AND (passoverride OR (AND of all pass checks))`. + +For example: + +``` +[[check]] + + # Ensure the scheduled task service is running AND + [[check.fail]] + type = 'ServiceUpNot' + name = 'Schedule' + + # Pass if the user runnning those tasks is deleted + [[check.passoverride]] + type = 'UserExistsNot' + name = 'CleanupBot' + + # OR pass if both scheduled tasks are deleted + [[check.pass]] + type = 'ScheduledTaskExistsNot' + name = 'Disk Cleanup' + [[check.pass]] + type = 'ScheduledTaskExistsNot' + name = 'Disk Cleanup Backup' + +``` + +The evaluation of checks goes like this: +1. Check if any Fail are true. If any Fail checks succeed, then we're done, the check doesn't pass. +2. Check if any PassOverride conditions pass. If they do, we're done, the check passes. +3. Check status of all Pass conditions. If they all succeed, the check passes, otherwise it fails. diff --git a/docs/config.md b/docs/config.md index bf0281a9..94262a8a 100644 --- a/docs/config.md +++ b/docs/config.md @@ -86,45 +86,4 @@ points = "-5" name = '/lib/systemd/system/sshd.service' ``` -## Combining check conditions -Using multiple conditions for a check can be confusing at first, but can greatly improve the quality of your images by accounting for edge cases and abuse. - -Given no conditions, a check does not pass. - -**Pass** conditions act as a logical AND with other pass conditions. This means they must ALL be true for a check to pass. - -**PassOverride** conditions act as a logical OR. This means that any can succeed for the check to pass. - -If any **Fail** conditions succeed, the check does not pass. - -So, it's like: ``((all pass checks) OR passoverride) AND fails``. - -For example: - -``` -[[check]] - - # Pass only if both scheduled tasks are deleted - [[check.pass]] - type = 'ScheduledTaskExistsNot' - name = 'Disk Cleanup' - [[check.pass]] - type = 'ScheduledTaskExistsNot' - name = 'Disk Cleanup Backup' - - # OR if the user runnning those tasks is deleted - [[check.passoverride]] - type = 'UserExistsNot' - name = 'CleanupBot' - - # AND the scheduled task service is running - [[check.fail]] - type = 'ServiceUpNot' - name = 'Schedule' -``` - -The evaluation of checks goes like this: -1. Check if any Fail are true. If any Fail checks succeed, then we're done, the check doesn't pass. -2. Check if any PassOverride conditions pass. If they do, we're done, the check passes. -3. Check status of all Pass conditions. If they all succeed, the check passes, otherwise it fails. diff --git a/docs/crypto.md b/docs/crypto.md index 5dbdb6f2..6345b3f8 100644 --- a/docs/crypto.md +++ b/docs/crypto.md @@ -1,6 +1,4 @@ -# aeacus - -## Adding Crypto +# Adding Crypto The public releases of `aeacus` ship with weak crypto (cryptographic security), which means that the encryption and/or encoding of scoring data files is not very "secure". diff --git a/docs/hints.md b/docs/hints.md new file mode 100644 index 00000000..b4315447 --- /dev/null +++ b/docs/hints.md @@ -0,0 +1,45 @@ +# Hints + +Hints let you provide information on failing checks. + +![Hint Example](../misc/gh/ReadMe.png) + +Hints are a way to help make images more approachable. + +You can add a conditional hint or a check-wide hint. A conditional hint is printed when the condition is executed and fails. Make sure you understand the check precedence; this can be trickey, as sometimes your check is NOT executed ([read about conditions](conditions.md)). + +Example conditional hint: +``` +[[check]] +points = 5 + + [[check.pass]] + type = "ProgramInstalledNot" + name = "john" + + [[check.pass]] + # This hint will NOT print unless the condition above succeeds. + # Pass conditions are logically AND-- they all need to succeed. + # If one fails, there's no reason to execute the other ones. + hint = "Removing just the binary is insufficient; use a package manager to remove all of a tool's files." + type = "PathExistsNot" + path = "/usr/share/john" +``` + +Check-wide hints are at the top level and always displayed if a check fails. Example check-wide hint: + +``` +[[check]] +hint = "Are there any 'hacking' tools installed?" +points = 5 + + [[check.pass]] + type = "ProgramInstalledNot" + name = "john" + + [[check.pass]] + type = "PathExistsNot" + path = "/usr/share/john" +``` + +You can combine check-wide and conditional hints. If the check fails, the check-wide hint is ALWAYS displayed, in addition to any conditional hints triggered. diff --git a/misc/gh/hint.png b/misc/gh/hint.png new file mode 100644 index 00000000..e55c7c36 Binary files /dev/null and b/misc/gh/hint.png differ