Controls overview

MFAudit evaluates security controls expressed in YAML. Every control maps to one or more mfpandas DataFrames, runs a query or Python logic, and produces a deterministic verdict.


Control libraries

File Controls Source
controls.yaml 46 CIS IBM z/OS, Db2, and CICS Benchmarks
example_controls.yaml yours Your own shop-specific rules

Multiple files can be merged at runtime:

mfaudit --controls controls.yaml example_controls.yaml --out out/

Execution engines

pandas_query

An optional filter expression is applied to the target DataFrame via pandas .query(), then an assertion block determines the verdict. Two assertion types are supported:

no_rows — PASS when the filtered result is empty; each surviving row is a finding.

implementation:
  engine: pandas_query
  dataset: irrdbu00.datasets
  select_columns: [DSBD_NAME, DSBD_UACC]
  filter: "DSBD_UACC == 'ALTER'"
  assertion:
    type: no_rows
    pass_message: "No dataset profiles with UACC=ALTER"
    fail_message: "Dataset profile(s) found with UACC=ALTER"

scalar_compare — reads the Value column of the first filtered row and compares it to expected using operator. {value} in messages is replaced with the actual value.

implementation:
  engine: pandas_query
  dataset: setropts.fieldInfo
  select_columns: [Setting, Value]
  filter: "Setting == 'INTERVAL'"
  assertion:
    type: scalar_compare
    operator: "<="
    expected: 90
    pass_message: "PASSWORD INTERVAL is {value} days (≤ 90)"
    fail_message: "PASSWORD INTERVAL exceeds 90 days (value: {value})"

Supported operators: <=, >=, <, >, ==, !=.

Use pandas_query when the test is a single filter + assertion. Use python for anything more complex.

See the full schema for all assertion fields and defaults.

python

Arbitrary Python logic runs with the full mfpandas namespace injected. The logic block must set status ('PASS', 'FAIL', 'REVIEW', 'SKIP') and optionally detail (string) and findings (list of dicts).

implementation:
  engine: python
  dataset: irrdbu00.users
  select_columns: [USBD_NAME, USBD_NOPWD]
  logic: |
    row = df[df['USBD_NAME'].str.upper() == 'MYSVC']
    if len(row) == 0:
        status = 'FAIL'
        detail = "STC userid MYSVC not found"
        findings = [{'USBD_NAME': 'MYSVC', 'status': 'NOT DEFINED'}]
    else:
        nopwd = row['USBD_NOPWD'].values[0]
        status = 'PASS' if nopwd == 'YES' else 'FAIL'
        detail = f"NOPASSWORD={nopwd}"
        findings = row.to_dict('records')

Available variables in logic:

Variable Type Content
df DataFrame The dataset named in dataset
users_df DataFrame irrdbu00.users
omvs_df DataFrame irrdbu00.userOMVS
groups_df DataFrame irrdbu00.groups
gomvs_df DataFrame irrdbu00.groupOMVS
access_df DataFrame irrdbu00.generalAccess
setropts object Full SETROPTS object
irrdbu00 object Full IRRDBU00 object
dcollect object Full DCOLLECT object (or None)
pd module pandas

Status values

Status Meaning
PASS All assertions satisfied
FAIL One or more findings; detail and rows shown in report
REVIEW Human judgment needed (e.g., list of TRUSTED accounts to validate)
SKIP Required data source not supplied; not counted as a failure
ERROR Control logic raised an exception

Data sources

Key mfpandas object Loaded from
setropts SETROPTS --setropts argument
irrdbu00 IRRDBU00 --irrdbu00 argument
dcollect DCOLLECT --dcollect argument (optional)

Controls declare which sources they need:

data_sources_needed: [setropts, irrdbu00]

If any required source is absent, the control is automatically marked SKIP.