Metadata-Version: 2.4
Name: netapp-audit-viewer
Version: 1.0.0
Summary: Python GUI viewer and live-tail tool for NetApp ONTAP CIFS Security Audit XML logs — stdlib only, no install.
Author-email: Umit Ozdemir <umitozdemir@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Ümit Özdemir
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/ozdemirumit/NetAppAuditViewer
Project-URL: Repository, https://github.com/ozdemirumit/NetAppAuditViewer
Project-URL: Issues, https://github.com/ozdemirumit/NetAppAuditViewer/issues
Project-URL: Changelog, https://github.com/ozdemirumit/NetAppAuditViewer/blob/main/CHANGELOG.md
Keywords: netapp,ontap,audit,xml,cifs,smb,security,tkinter,gui,live-tail
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: X11 Applications
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: MacOS X
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Logging
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# NetApp Audit XML Viewer

A single-file Python GUI for browsing **NetApp ONTAP CIFS Security Audit XML** logs. Opens large files instantly, follows them like `tail -f` as new events are written, and lets you slice the data by user, IP, time range, and event type — with no external dependencies beyond Python's standard library.

Built for storage and infrastructure engineers who need to triage `audit_*.xml` output without spinning up Splunk/ELK.

![status: working](https://img.shields.io/badge/status-working-brightgreen) ![python: 3.10+](https://img.shields.io/badge/python-3.10%2B-blue) ![deps: stdlib only](https://img.shields.io/badge/deps-stdlib%20only-success) ![license: MIT](https://img.shields.io/badge/license-MIT-lightgrey)

---

## Features

- **Live tail** — watches the XML file and streams new events to the table as they're written, with proper handling of partial events that get split across writes.
- **Rotated file support** — point it at a folder and it loads `audit.xml`, `audit.xml-1`, `audit.xml.0` etc. in chronological order, then tails the newest one.
- **Unified columns for both event families** — logon events (4624/4625/4634) and file-operation events (4656/4663/4670/4907) share a single `User` / `IP` / `Object` / `Action` view, so you don't have to know which schema applies.
- **Filters** — Event ID, Result, User, IP, time range (with `Last 1 hour` / `Last 24 hours` shortcuts), and a free-text search across all fields. Filters apply as you type (debounced).
- **Right-click drill-in** — right-click any row to instantly filter by that IP/user, or run a combined "this IP + this user, failures only" pivot. Also copies values to clipboard.
- **Statistics window** — top failure IPs, top failure users, hourly distribution as ASCII bar chart, double-click to filter the main table.
- **CSV export** — UTF-8 with BOM and `;` delimiter, opens cleanly in Excel with a Turkish or other non-ASCII locale.
- **CLI flags** — pre-load filters from the command line, e.g. for shortcut-driven workflows.
- **No installation, no dependencies** — single `.py` file, only Python stdlib (`tkinter` ships with Python on Windows by default).

---

## Quick start

```bash
python netapp_audit_viewer.py
python netapp_audit_viewer.py /path/to/audit_log.xml
python netapp_audit_viewer.py /path/to/audit_log.xml --tail
python netapp_audit_viewer.py /path/to/audit_log.xml --filter-ip 10.0.0.5 --only-failures --tail
```

That's it. No `pip install`, no virtual env. Works on Windows, Linux and macOS.

### CLI options

| Flag | Description |
|---|---|
| `path` (positional) | XML file to open on startup. |
| `--tail`, `-t` | Auto-open the file and start tailing immediately. |
| `--filter-ip IP` | Pre-fill the IP-contains filter. |
| `--filter-user USER` | Pre-fill the user-contains filter. |
| `--filter-eventid ID` | Pre-filter by a specific Event ID (e.g. `4625`). |
| `--only-failures` | Show only `Audit Failure` events on startup. |

---

## Supported events

The parser recognises two families of NetApp CIFS audit events and exposes them through unified columns:

| Event ID | Name | Family | Key fields |
|---|---|---|---|
| 4624 | Logon Attempt | Logon | `TargetUserName`, `IpAddress` |
| 4625 | Logon Failure | Logon | `TargetUserName`, `IpAddress`, `FailureReasonString` |
| 4634 | Logoff | Logon | `TargetUserName`, `IpAddress` |
| 4656 | Open Object | File-op | `SubjectUserName`, `SubjectIP`, `ObjectName`, `DesiredAccess` |
| 4663 | Get Object Attributes | File-op | `SubjectUserName`, `SubjectIP`, `ObjectName`, `InformationRequested` |
| 4670 | Permissions Changed | File-op | `SubjectUserName`, `ObjectName`, `OldSD`, `NewSD` |
| 4907 | Auditing Settings Changed | File-op | `SubjectUserName`, `ObjectName`, `OldSD`, `NewSD` |

Other event types are still parsed and shown — only the unified `User`/`IP`/`Object`/`Action` columns may be empty for unrecognised IDs.

---

## Real-world example

Here's a quick triage on a 54 MB / 54,806-event audit file from a production CIFS share:

```
Event distribution
  4625 Logon Failure          49,839
  4624 Logon Attempt           3,003
  4634 Logoff                  1,841
  4656 Open Object                54
  4663 Get Object Attributes      52
  4907 Auditing Settings           9
  4670 Permissions Changed         8

Top failure IPs
  172.17.10.165   37,350  (74.9%)   <- single host, almost the whole file
  172.17.10.150    3,625   (7.3%)
  172.17.9.9       2,047   (4.1%)

Top failure usernames
  tb2783          37,350  (74.9%)   <- repeating against the same SVM
  tb1386           3,675   (7.4%)

Top failure reasons
  No such user account                              44,186  (88.7%)
  User name is correct but the password is wrong    3,858   (7.7%)
  Unknown user name or bad password                 1,780   (3.6%)
```

A single host hammering one username with `No such user account` failures over multiple days — classic credential-stuffing pattern. From the GUI, this is two clicks: open the Statistics window → double-click `172.17.10.165` in the Failure IPs tab → main table is filtered to that IP for further review or CSV export to the SOC team.

---

## Build a Windows `.exe`

If you want to ship this to colleagues who don't have Python installed, use [PyInstaller](https://pyinstaller.org):

```cmd
pip install pyinstaller
pyinstaller --windowed --name "NetAppAuditViewer" --clean netapp_audit_viewer.py
```

The result is in `dist\NetAppAuditViewer\` (about 12 MB compressed). Zip the folder and you're done.

A ready-made `build.bat` is included in the repo — double-click it.

**Notes:**
- `--onefile` produces a single `.exe`, but corporate antivirus often quarantines PyInstaller's onefile bootloader. The default `--onedir` build is more AV-friendly.
- Add `--icon=audit.ico` if you want a custom icon.
- For a tiny portable alternative when target machines have Python: `python -m zipapp . -m "netapp_audit_viewer:main" -o NetAppAuditViewer.pyz` produces a ~40 KB single-file zipapp.

---

## How tail works (technical note)

The tail thread reads the file in chunks starting from the last position, keeps a buffer of unparsed bytes, and emits parsed events to the GUI thread via a `queue.Queue`. If a write happens mid-event, the partial bytes stay in the buffer until the closing `</Event>` arrives — no malformed records ever reach the table. File rotation/truncation is detected by comparing on-disk size to the last read position.

The GUI never blocks on disk I/O. Filtering is debounced (250 ms) so you can type freely without freezing the UI on a 100k-event dataset.

---

## Limitations

- The Treeview shows at most 10,000 rows at a time (configurable via `MAX_DISPLAY_ROWS` at the top of the file). All events are kept in memory; only the displayed slice is capped for rendering performance.
- Memory footprint is roughly 1.5 KB per event, so 1M events ≈ 1.5 GB RAM. For larger datasets, filter on import or split the file first.
- Dates and times are interpreted as the literal string in `TimeCreated/@SystemTime` (UTC in NetApp's output). No timezone conversion.

---

## License

MIT — see [LICENSE](LICENSE).

## Contributing

Issues and PRs welcome. Particularly interested in:
- Coverage of additional NetApp event IDs (NFS audit, anti-virus events, etc.)
- Performance improvements for very large files (memory-mapped reading?)
- Optional Splunk/ELK forwarder mode
