Report whether a returned drive was wiped and scanned

The USB audit answered who had which drive and when, but not whether it came
back wiped or virus-scanned. That is the question the record is kept to answer:
removable-media custody is CMMC evidence, and custody without the state of the
media on return is half the story. Both stores already held the values - the
external checkinoutlog in sanitized and scanned_viruses, the app's own
usbcheckouts in waswiped and scannedviruses - and neither mode selected them.

Both now report them, in the JSON, the CSV and the column list, so the two
directory modes still answer in one voice.

Two things the rendering has to get right:

- Only a return carries them. The wipe and the scan happen when the drive comes
  back, so a check-out row shows blanks rather than implying a fact about an
  event that had not happened yet.
- Unknown is not "no". Both stores keep these nullable deliberately: a null
  means nobody wrote it down, which is a different claim from a drive that came
  back unwiped. Rendering an unrecorded return as "No" would put an assertion
  in an audit record that no one ever made, so unknown stays blank and reads as
  the gap it is. A test holds that, because "no" is the tempting default.

auditflag lives in selfhosted.py rather than routes.py: routes already imports
selfhosted, so the helper had to sit on that side or the import would be
circular.
This commit is contained in:
cproudlock
2026-09-16 15:41:13 -04:00
parent 0f56653345
commit 35d4fcd41c
3 changed files with 97 additions and 4 deletions

View File

@@ -1209,7 +1209,8 @@ def _auditcsv(rows, columns, filename):
AUDITCOLUMNS = ['timestamp', 'action', 'device_id', 'device_desc', 'badge',
'name', 'locker_location']
'name', 'locker_location', 'sanitized', 'scanned_viruses']
@usb_bp.route('/reports/audit', methods=['GET'])
@@ -1235,7 +1236,8 @@ def report_audit():
try:
with conn.cursor() as cur:
sql = ('SELECT l.timestamp, l.action, l.device_id, l.badge_number, '
'l.locker_location, d.device_desc, '
'l.locker_location, l.sanitized, l.scanned_viruses, '
'd.device_desc, '
'u.first_name, u.last_name '
'FROM checkinoutlog l '
'LEFT JOIN devices d ON d.device_id = l.device_id '
@@ -1263,6 +1265,13 @@ def report_audit():
'badge': row.get('badge_number') or '',
'name': name,
'locker_location': row.get('locker_location') or '',
# Only a return can carry these: the wipe and the scan
# happen when the drive comes back, so a check-out row
# showing them would invent a fact.
'sanitized': (selfhosted.auditflag(row.get('sanitized'))
if row.get('action') == 'check-in' else ''),
'scanned_viruses': (selfhosted.auditflag(row.get('scanned_viruses'))
if row.get('action') == 'check-in' else ''),
})
finally:
conn.close()