.audit files that can be reproduced faithfully. See Import a Tenable audit file.
What a check can and cannot do
A check is declarative and read-only. It can look at:- whether a file exists, its mode, owner and group;
- whether a file’s lines match, or do not match, a regular expression, optionally comparing a value captured from the line;
- a Windows registry value under
HKEY_LOCAL_MACHINE; - a Windows password, lockout, audit or user-rights setting;
- a Linux kernel parameter (
sysctl); - a service’s configured start state (systemd, Windows services, launchd);
- whether an operating-system package is installed.
Requirements
- A server running the agent with the server profile, in an agent release that includes custom audits. A server on an older agent shows Not assessed (agent too old) for the audit, never a pass. See Update an existing agent.
- The Custom audits collector enabled in the organization’s collector policy. It is on by default.
- Windows policy checks (
password_policy,lockout_policy,audit_policy,user_right) read the facts the hardening collector already gathers, so they need the agent in privileged mode. Without it they show Not assessed. - An owner or admin role to create, import, edit or assign an audit. Every member can read audits and results.
Create an audit
Open Infra Vulnerabilities, then Hardening, then Custom audits. Choose New audit, name it, pick the platform, and edit the checks as JSON. Check validates the document without saving it and lists every problem at once. The platform cannot be changed after creation, because results and assignment are per platform. A new audit reaches no server until you assign it. Choose Assign, then either all servers of the audit’s platform or specific scan groups. A scan group that is disabled matches nothing. The assignment takes effect at each server’s next scan. Editing the checks raises the audit’s version. Each server reports which version it judged, and a result from an earlier version is shown as such until the server scans again. Renaming an audit, enabling or disabling it, and changing its assignment do not change the version.The format
An audit’s checks are a JSON object with achecks list.
id (lower case letters, digits, dot, dash and underscore, at most 64 characters, unique in the audit), a title, and a test. It can also carry severity (low, medium, high or critical), description, remediation, reference and when. A field that a test type does not use is an error, not ignored, so a check can never pass on a condition its author thought it also verified.
Test types
| Type | Platforms | Fields | Passes when |
|---|---|---|---|
file_exists | all | path, exists | at least one file matches path (exists: true), or none does (exists: false) |
file_content | all | path, match, pattern, expect, min_matches, capture, missing | see file content |
file_mode | Linux, macOS | path, exact_mode or max_mode, owners, groups, missing | every matched file has the mode, owner and group given |
registry | Windows | key, value_name, exists or compare, missing | the value exists, or satisfies the comparison |
password_policy | Windows | setting, compare, missing | the setting satisfies the comparison |
lockout_policy | Windows | setting, compare, missing | the setting satisfies the comparison |
audit_policy | Windows | subcategory, compare, missing | the audit setting satisfies the comparison |
user_right | Windows | right, holders | the accounts holding the right match holders |
sysctl | Linux | key, compare, missing | the kernel parameter satisfies the comparison |
service | all | name, states | the service’s configured state is one of states |
package | Linux | name, exists | the package is installed (true) or not (false) |
Comparisons
Acompare has an op and a value:
op | Value | Meaning |
|---|---|---|
eq, ne | value | equal or not equal. Numbers compare as numbers; text ignores case and runs of whitespace. |
lt, le, gt, ge | value, a number | numeric comparison. A value that is not a number is Not assessed, not a failure. |
between | value as "low..high" | numeric, inclusive on both ends |
matches, not_matches | value, an RE2 expression | regular expression match |
in, not_in | values, a list | the value is, or is not, one of the list |
File content
file_content reads a file line by line. pattern selects lines. With match: "present", at least min_matches lines (default 1) must match, and if expect is given, every selected line must also match expect. With match: "absent", no line may match pattern and expect. With capture, the pattern needs exactly one capture group and its text is compared with the capture comparison.
When path contains a wildcard, the matched files are judged together as one stream, so a setting may be in any of /etc/modprobe.d/*.conf. A missing value of pass, fail, not_applicable or not_assessed says what a missing file or setting means; the default is fail.
Paths and wildcards
A path is absolute. On Windows it starts with a drive (C:\) or one of %SystemRoot%, %SystemDrive%, %ProgramFiles%, %ProgramFiles(x86)%, %ProgramData% and %windir%. * and ? are allowed in at most two path segments, never in the first, and ** is not supported. .. is not allowed. These rules, with the limits below, are what keep a wildcard from becoming a walk of the whole disk.
Windows policy settings
| Test | setting values |
|---|---|
password_policy | min_length, max_age_days, min_age_days, history_size, complexity, reversible_encryption |
lockout_policy | threshold, duration_minutes, reset_minutes |
audit_policy takes a lower-case subcategory name such as credential_validation or logon, and compares against none, success, failure or success_failure. The agent reports the subcategories it collects; any other shows Not assessed.
user_right takes a right such as SeInteractiveLogonRight and holders with an op of none, only, includes or excludes and a list of SIDs. Rights are compared by SID, not by account name.
Service states
states is a list of enabled, disabled, manual, masked and not_installed. This is the configured state: whether the service starts at boot, read from the systemd unit files, the Windows service start type, or the launchd override database. It does not say whether the service is running at this moment.
Applicability
A check can carry awhen block so it only applies where it makes sense:
match is all or any, and negate: true inverts the result. When the condition is not met the check is Not applicable. When the condition cannot be decided, the check is Not assessed.
Results
| Result | Meaning |
|---|---|
| Pass | The server meets the check. |
| Fail | It does not. Evidence shows the observed value where one can be shown. |
| Not applicable | The check’s when condition is not met, or its missing setting says so. |
| Not assessed | The check could not be judged on this server: a path on the deny-list, a limit that was reached, a setting the host did not report. This is unknown, never a pass. |
| State | Meaning |
|---|---|
| Assessed | The server judged every check of the current version. |
| Assessed against an older version | It judged an earlier version and will re-judge at its next scan. |
| Waiting for the next scan | It has not scanned since the audit was assigned. |
| Not assessed | The server could not judge the audit as a whole; the reason is shown. |
| Not assessed (agent too old) | Its agent has never asked for custom audits, so it predates the feature. Upgrade the agent. This is not a pass. |
| Not delivered | A server receives at most 1000 checks across all its audits, and this audit did not fit. Narrow the assignment or split the audits. This is not a pass. |
What leaves the server
The agent judges each check on the server. Only these leave it:- the verdict;
- a short reason, chosen from a fixed list of sentences;
- evidence of at most 256 characters, with credential-shaped content masked.
What the agent will never read
An audit is delivered from the platform to every assigned server, so the agent enforces limits that no document can change. A verdict alone is not safe for a file that holds a secret: a check that asks whether the second field of a password-hash line starts with a given prefix reveals the hash one bit at a time. The agent therefore refuses the file, whatever the check says.| Class | What it covers | What a check can do |
|---|---|---|
| Never touched | The agent’s own state and credentials; the Windows registry hive files (SAM, SECURITY, SYSTEM, SOFTWARE) and their backups; Active Directory’s database; DPAPI keys and credential vaults; browser profiles; keychains; /proc/<pid> state such as environ and mem; /dev; container and orchestrator secret mounts | nothing, not even whether the path exists |
| Content never read | /etc/shadow and /etc/gshadow; private keys and key stores (*.pem, *.key, *.p12, *.pfx, *.jks, SSH private keys); .netrc, .pgpass, cloud and Kubernetes credential files; dotenv files; shell history | existence, mode, owner and group only. This is what lets you check that /etc/shadow is mode 0640. |
HKLM\SAM and HKLM\SECURITY can never be read. A registry value whose name looks like a credential (for example one containing password or secret) is compared only when it is a number. Symbolic links are followed before the deny-list is applied, so a link to a denied file is denied.
Three more rules apply to file content:
- A line that assigns a credential-shaped setting, such as
db_password = ...orapi_token: ..., is never evaluated: no pattern is run against it, so no verdict can depend on it. A setting with a switch or a small number as its value, such asPasswordAuthentication noorMinimumPasswordLength = 14, is still audited. The evidence says how many credential-shaped lines were skipped. - A file that looks binary, or that begins like key material, is not read.
- A check cannot be satisfied or violated by a skipped line. This is the cost of the protection, and it is why the protection is by line and not by file.
Limits
The console enforces these when you save, and the agent enforces them again. The agent rejects the whole document if any is exceeded.| Limit | Value |
|---|---|
| Audits per organization | 25 |
| Checks per audit | 200 |
| Checks delivered to one server | 1000 |
Conditions in one when | 8 |
| Regular expression length | 512 characters |
| Path length | 1024 characters |
| Bytes read per file | 1 MiB (a file larger than this is judged only as far as it proves something) |
| Files per wildcard | 64 |
| Entries listed per directory | 5000 |
| Files read per scan | 2000 |
| Bytes read per scan | 32 MiB |
| Time for all custom audits in one scan | 60 seconds |
| Evidence per check | 256 characters |
Import a Tenable audit file
Choose Import Tenable audit, then choose the file or paste its text. The import is a preview first: every item in the file appears in a table, marked Imported or Skipped with the reason, and nothing is saved until you confirm. Nothing is silently dropped. The imported audit is not assigned to any server until you assign it, and the skipped items are kept with the audit so you can still see them. Only Unix and Windows files can be imported. A Unix file becomes a Linux audit by default; you can choose macOS.What is imported
| Tenable item | Becomes | ||
|---|---|---|---|
FILE_CHECK with owner, group and mode or mask (Unix) | file_mode. A mask is read as a umask: the maximum mode allowed is its complement. ` | ` alternatives become a list. | |
FILE_CHECK_NOT with only file (Unix) | file_exists with exists: false | ||
FILE_CONTENT_CHECK with regex and expect | file_content present: the lines regex selects must also match expect. min_occurrences becomes min_matches. | ||
FILE_CONTENT_CHECK_NOT | file_content absent | ||
RPM_CHECK with an explicit required: YES or NO and no version | package installed or not installed | ||
REGISTRY_SETTING and REG_CHECK under HKLM | registry. POLICY_DWORD ranges such as [1..30] and [15..MAX], sets, POLICY_TEXT, and POLICY_SET (Enabled is read as 1 and Disabled as 0). CAN_BE_NULL becomes missing: pass. | ||
PASSWORD_POLICY and LOCKOUT_POLICY | password_policy, lockout_policy | ||
AUDIT_POLICY_SUBCATEGORY | audit_policy | ||
USER_RIGHTS_POLICY | user_right, when every account is a SID or a well-known group such as Administrators or Authenticated Users. No One becomes none. | ||
SERVICE_POLICY | service, comparing the start type | ||
FILE_CHECK with file_option MUST_EXIST or MUST_NOT_EXIST (Windows) | file_exists | ||
<if>, <condition>, <then>, <else> | a when block on the items in each branch; the <else> branch is the negation. A forced PASSED report is noted: the checks outside a taken branch report Not applicable, which stands in for it. |
description becomes the title, info the description, solution the remediation, reference and see_also the reference, and severity the severity. The check’s id is generated from its position and title.
What is skipped, and why
| Skipped | Reason |
|---|---|
CMD_EXEC, AUDIT_POWERSHELL, WMI_POLICY, AUDIT_XML, MACOSX_DEFAULTS_READ, and any item with a cmd, powershell or WMI field | Command checks are not supported. A check that runs something is exactly what a compromised console account would use to attack every server, so the format cannot express it. |
An <if> whose condition cannot be imported | Its branches are skipped with the reason, because they would otherwise run without the guard. |
A forced WARNING or FAILED report | Results come from the checks themselves. |
Content checks on a path on the deny-list, such as /etc/shadow | The content is never read. |
| A regular expression that RE2 cannot compile | Lookahead, lookbehind and backreferences. |
PROCESS_CHECK, open-port checks, CHKCONFIG, XINETD_SVC | They need a process or socket listing, or run levels, which are not supported. |
PKG_CHECK, items for another system such as SunOS | The agent does not cover that platform. |
RPM_CHECK with a version or an operator, or without required | Version comparison is not supported, and without required the meaning is ambiguous. |
Registry values under HKU or other per-user hives, reg_enum, binary, multi-string and ACL values | They are not the server’s configuration, or cannot be compared. |
Windows ACL, md5, uid, gid and attribute checks | Not supported. |
BANNER_CHECK | Use a file_content check on the banner file or a registry check on the legal notice value. |
| An account name with no well-known SID | The agent reports SIDs, not account names, so the comparison cannot be made faithfully. |
| Any field that is not understood | A field that carries meaning is never ignored, because an item that imports but means something else gives a false pass or a false failure. |
\\ is one backslash and \" is a quote. Regular expressions written for a POSIX engine usually work as they are.