--- # ══════════════════════════════════════════════════════════════════════ # TEMPLATE: File Permission / Ownership Check # ══════════════════════════════════════════════════════════════════════ # # Uses ansible.builtin.stat — no shell command needed. # Prefer this over shelling out to stat(1) for file attribute checks. # The stat module returns a structured dict with typed values, which # makes the 'passed' expression straightforward and readable. # # Useful stat attributes: # stat.exists — bool: file is present # stat.mode — string: octal permissions, e.g. '0640' # stat.pw_name — string: owning user name, e.g. 'root' # stat.gr_name — string: owning group name, e.g. 'shadow' # stat.size — int: file size in bytes # stat.isreg — bool: is a regular file # stat.isdir — bool: is a directory # stat.islnk — bool: is a symlink # # Common 'passed' expression patterns: # # # File exists with exact owner/group/mode: # 'passed': ( # _stat.stat.exists and # _stat.stat.pw_name == 'root' and # _stat.stat.gr_name == 'root' and # _stat.stat.mode == '0640' # ), # # # File must NOT exist: # 'passed': not _stat.stat.exists, # # # File must be a regular file (not a symlink) with tight permissions: # 'passed': ( # _stat.stat.exists and # _stat.stat.isreg and # not _stat.stat.islnk and # _stat.stat.mode in ['0400', '0440', '0600'] # ), # # ══════════════════════════════════════════════════════════════════════ # ── SUITE_ID: SHORT_DESCRIPTION ───────────────────────────────────── - block: - name: "Gather: Stat /path/to/file" ansible.builtin.stat: path: /path/to/file register: _stat - name: "Evaluate: TEST_ID" ansible.builtin.set_fact: test_results: "{{ test_results + [{ 'test_id': 'TEST_ID', 'category': 'FR_NUMBER — CATEGORY_NAME', 'requirement': 'SR X.Y — REQUIREMENT_NAME', 'description': '/path/to/file shall be owned by root:root with mode 0640', 'passed': ( _stat.stat.exists and _stat.stat.pw_name == 'root' and _stat.stat.gr_name == 'root' and _stat.stat.mode == '0640' ), 'expected': 'root:root 0640', 'actual': ( (_stat.stat.pw_name + ':' + _stat.stat.gr_name + ' ' + _stat.stat.mode) if _stat.stat.exists else 'FILE NOT FOUND' ), 'severity': 'high', 'remediation': 'chown root:root /path/to/file && chmod 0640 /path/to/file' }] }}" ignore_errors: yes