cyber-0 updated with more examples
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
---
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# TEMPLATE: Human-in-the-Loop (HITL) Test
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Use when a control cannot be evaluated automatically and requires a
|
||||
# human reviewer to observe evidence and record a verdict.
|
||||
#
|
||||
# Examples of controls that need HITL:
|
||||
# - Physical access controls / badge logs
|
||||
# - Operator training records
|
||||
# - Network diagram review
|
||||
# - Custom application security configuration
|
||||
# - Vendor-specific proprietary interfaces
|
||||
#
|
||||
# How it works (Ansible-native):
|
||||
# 1. Gather tasks run against the remote host as normal.
|
||||
# 2. ansible.builtin.debug displays the evidence on the console.
|
||||
# 3. ansible.builtin.pause with 'delegate_to: localhost' prompts the
|
||||
# reviewer on the control node, regardless of the remote target.
|
||||
# For multi-host runs, the prompt fires once per host so each
|
||||
# target gets an independent human verdict.
|
||||
# 4. The reviewer's verdict (pass/fail/skip) and any notes are
|
||||
# captured in the test_results[] record alongside the raw evidence.
|
||||
#
|
||||
# 'passed' field values used here:
|
||||
# true — reviewer entered 'pass' or 'p'
|
||||
# false — reviewer entered 'fail' or 'f'
|
||||
# 'skipped' — reviewer pressed Enter or entered 'skip'/'s'
|
||||
#
|
||||
# All three values are handled by the report renderers.
|
||||
#
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── SUITE_ID: SHORT_DESCRIPTION ─────────────────────────────────────
|
||||
|
||||
- block:
|
||||
# ── Gather evidence from the remote host ────────────────────────
|
||||
- name: "Gather: [HITL] DESCRIBE_WHAT_IS_COLLECTED"
|
||||
ansible.builtin.shell: |
|
||||
# Collect the evidence the reviewer needs to make a decision.
|
||||
# Keep output focused: show only what is relevant to the check.
|
||||
echo "Replace with your evidence-gathering command"
|
||||
register: _hitl_evidence
|
||||
changed_when: false
|
||||
|
||||
# ── Present the evidence to the reviewer (appears in Ansible log) ─
|
||||
- name: "Display: [HITL] TEST_ID — evidence for review"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
══════════════════════════════════════════════════════════════
|
||||
MANUAL REVIEW REQUIRED · TEST_ID · {{ inventory_hostname }}
|
||||
══════════════════════════════════════════════════════════════
|
||||
Requirement : SR X.Y — REQUIREMENT_NAME
|
||||
Check : DESCRIPTION
|
||||
|
||||
Evidence
|
||||
────────
|
||||
{{ _hitl_evidence.stdout | default('(no output collected)') | indent(1) }}
|
||||
══════════════════════════════════════════════════════════════
|
||||
|
||||
# ── Reviewer enters verdict on the control node ─────────────────
|
||||
# delegate_to: localhost ensures the prompt appears locally even
|
||||
# when this playbook targets remote hosts.
|
||||
- name: "Prompt: TEST_ID — verdict for {{ inventory_hostname }}"
|
||||
ansible.builtin.pause:
|
||||
prompt: |
|
||||
Review the evidence above for {{ inventory_hostname }}.
|
||||
Does it satisfy SR X.Y — REQUIREMENT_NAME?
|
||||
Enter verdict [pass / fail / skip]:
|
||||
register: _hitl_verdict
|
||||
delegate_to: localhost
|
||||
|
||||
# ── Capture reviewer notes on failure ───────────────────────────
|
||||
# This task only runs when the verdict is fail/f, so _hitl_notes
|
||||
# may be undefined for pass/skip results. The evaluate task below
|
||||
# uses 'is defined' to handle this safely.
|
||||
- name: "Prompt: TEST_ID — notes for {{ inventory_hostname }} (fail only)"
|
||||
ansible.builtin.pause:
|
||||
prompt: "Describe the gap or finding (required for audit trail):"
|
||||
register: _hitl_notes
|
||||
delegate_to: localhost
|
||||
when: _hitl_verdict.user_input | lower | trim in ['fail', 'f']
|
||||
|
||||
# ── Evaluate: record verdict + evidence in test_results[] ───────
|
||||
- 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': 'DESCRIPTION',
|
||||
'passed': (
|
||||
'skipped'
|
||||
if (_hitl_verdict.user_input | lower | trim in ['skip', 's', ''])
|
||||
else (_hitl_verdict.user_input | lower | trim in ['pass', 'p'])
|
||||
),
|
||||
'expected': 'Reviewer confirmed control is in place',
|
||||
'actual': _hitl_evidence.stdout | trim | default('(no evidence collected)', true),
|
||||
'severity': 'SEVERITY',
|
||||
'remediation': 'REMEDIATION',
|
||||
'reviewer': ansible_user_id,
|
||||
'notes': (_hitl_notes.user_input | trim)
|
||||
if _hitl_notes is defined
|
||||
else ''
|
||||
}] }}"
|
||||
ignore_errors: yes
|
||||
Reference in New Issue
Block a user