NETSCALER TRIAGE Offline log triage for NetScaler ADC and Gateway. Give it a log folder or archive. It prepares the input, searches for suspicious patterns and public indicators, explains matching rules and decoded command text, finds update/version records, and links related events on the same appliance. Original files are not modified. Payloads are never executed or fetched. INSTALL Requires Python 3.14 or newer. From the unpacked source folder, on macOS/Linux: python3 -m venv .venv source .venv/bin/activate Or on Windows PowerShell: py -m venv .venv .venv\Scripts\Activate.ps1 Then install the local project: python -m pip install . A built wheel can instead be installed with python -m pip install PATH_TO_WHEEL. For a locked developer installation, use uv sync --locked with a suitable installed Python interpreter. The lockfile records exact dependency versions. Installation may need internet access; analysis itself runs offline. START HERE netscaler-triage logs.7z Or use an unpacked folder: netscaler-triage ./logs Or run inside the folder containing your logs: netscaler-triage ZIP, TAR, TAR.GZ/TGZ, GZIP and 7z are detected automatically, including nested archives. Unsupported formats are reported. Several inputs can be combined: netscaler-triage ./appliance-a ./appliance-b another-export.zip The result folder is created automatically and printed when the run finishes. --output PATH chooses a new result folder. Existing results are never overwritten. Appliance groups are inferred from folder structure; review the preparation manifest when a combined export has an unusual layout. For an encrypted archive, request a hidden password prompt: netscaler-triage logs.7z --password --password VALUE also works, but may expose the password in shell history or process listings. The same password is used for nested archives. 7z, ZipCrypto and AES-ZIP are supported. Passwords are not saved in results. WHAT TO READ FIRST summary.json tells you what was processed and whether anything was incomplete. findings.jsonl contains selected events, original source/line references, matching rule details, extracted indicators and safe decoding steps with explanations. Each line is a separate JSON object. Explanations describe what command text could do; they do not establish that it ran successfully. IOCS.csv contains exact values for SIEM searches and a separate defanged display value. Observed values are candidates, not an automatic blocklist. updates.jsonl contains update/version evidence. An installer command is not by itself proof of a completed patch. correlations.jsonl contains temporal candidates between log files on the same appliance, not proof of causation. Other outputs support verification: evidence.sqlite On-disk event index coverage.json Per-file coverage and read/matching errors input_manifest.json Original relative paths and SHA-256 checksums configuration.json Effective rules, references and time assumptions indicator_hits.csv Matches against configured reference indicators file_hash_hits.json Reference matches against original file SHA-256 preparation_manifest.json Preparation actions, groups and exclusions inputs/ Prepared inputs when automatic preparation runs URL/Base64 transformations retain the original value, transformation, parent step, decoded result or error, and checksum. Binary decoded content is displayed as hex and is not executed. Finding rule_details retain matching rule descriptions and source metadata; custom packs can provide their own explanations. ADD YOUR OWN RULES OR IOCS Bundled public references are loaded automatically. They are a dated, attributed collection, not a claim to cover every known attack. No private case addresses, appliance identities or environment-specific assumptions are included in defaults. References are not downloaded during a scan. netscaler-triage ./logs --config examples/rules.yml netscaler-triage ./logs --ioc-file indicators.txt netscaler-triage ./logs --pattern "(?i)your_search_expression" Repeat --config or --ioc-file to combine packs. An explicit --config directory loads supported packs recursively in sorted relative-path order; no private or customer directory is loaded unless you select it. netscaler-triage ./logs --config ./my-rules Sigma YAML is the primary rule format, parsed with pySigma and evaluated locally with the supported subset in RULES.txt. Unsupported features are rejected explicitly. Defaults include 47 Sigma rules and 246 deduplicated public reference indicators. THREAT_INTELLIGENCE.txt indexes all 52 sources, uses and limitations; config/threat_intel.json is the complete machine-readable inventory. JSON, CSV and one reference per line in TXT remain supported. TXT infers clear IP addresses, SHA-256 hashes, HTTP(S) URLs and absolute Unix paths; other text stays literal. Explicit CSV/JSON types are unchanged. An exported IOCS.csv can be loaded again. Examples contain synthetic values, not real intelligence. Keep local case rules under private/, which is gitignored and excluded from release builds; load them explicitly with --config private/your_rules.yml. Private packs are never discovered or enabled automatically. Example files are included in the source release. Rule packs are data files. Python plugin code is not loaded from them. TIME AND DEVICE IDENTITY Explicit timestamps are retained. A missing year or timezone is not guessed. For known time settings and explicit appliance names on prepared folders: netscaler-triage scan --input ADC-A=./logs --year 2026 --timezone ADC-A=UTC Use your actual device settings; UTC and the year above are examples only. Ambiguous daylight-saving times remain unresolved. Time assumptions and per-file year overrides can be supplied using --time-config; see examples/time.json. LIMITS AND INCOMPLETE RESULTS Exit 0 means the configured processing completed, not that the appliance is clean. Exit 2 means an error or incomplete processing; inspect summary.json. Invalid decoding candidates may be ordinary text, not malicious payloads. Unknown text formats remain searchable, even if no timestamp is parsed. Proprietary binary logs require an appropriate external decoder. Unreadable files, damaged archives and regex timeouts are recorded while other valid inputs continue. Path traversal, unsafe links and exhausted archive quotas stop preparation. A controlled interruption preserves the processed database where possible. There is no automatic resume after interruption. A single worker reads lines incrementally. SQLite stores events and large sorts on disk; small decoding caches are bounded. One CPU core can be busy. This is not a hard process-wide RAM or CPU quota: archive metadata, large rule lists and file inventories need additional memory. Allow disk space for preparation and results. Defaults: 2 GiB preparation budget, 100,000 archive entries, nesting depth 3, 1 MiB per line and 512 MiB expanded per log file. Limits never mean a silent successful scan. Advanced options can raise them for legitimate larger inputs. HELP AND OPTIONAL COMMANDS netscaler-triage --help netscaler-triage scan --help-all netscaler-triage extract logs.7z prepared netscaler-triage decode payload.txt netscaler-triage indicators text.txt netscaler-triage inventory ./logs Short help shows common options. Advanced help includes resource limits, time assumptions and --no-default-patterns, which disables built-in rules and bundled public references; explicitly supplied packs still apply. DEVELOPMENT AND RELEASE uv run python -W error -m unittest discover -s tests uv run mypy uv run ruff check src tests typing_stubs uv run ruff format --check src tests typing_stubs uv build Synthetic tests cover parsing, archive safety, decoding, matching, correlations and packaging hygiene. VALIDATION.txt records what was actually tested. Runtime environments and analysis outputs are not release files. Source distributions use an explicit allowlist to avoid bundling unrelated documents or case settings. LICENSE Project code is licensed under the MIT License; see LICENSE.txt. Third-party dependencies and referenced external materials retain their own licenses. The project does not relicense original external publications. VALIDATE PACKS BEFORE A SCAN netscaler-triage rules --config ./my-rules This offline command loads and compiles the bundled defaults and selected packs, then prints the Sigma rule, regex rule and explicit active indicator counts. It does not prepare or scan logs, contact external services or create artifacts. Use --no-default-patterns to validate only the selected files; --json gives a machine-readable result. Invalid Sigma, unsupported rules and conflicting rule identifiers fail validation. Syntax validation does not prove detection quality. Directory discovery skips README.txt, LICENSE.txt and CHANGELOG.txt documents (case-insensitive); an explicitly named TXT file still loads as references using TXT type inference.