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.txt is the readable overview: status, counts, limitations and next steps. FINDINGS.csv contains only rule/indicator matches, explanations, source references and indicator statuses. A match is not proof of execution or compromise. IOCS.csv contains raw search candidates and defanged display values, not a blocklist. Technical exports are grouped under details/: findings.jsonl (all selected events, including context/errors), summary.json, updates.jsonl, correlations.jsonl, coverage.json, input_manifest.json, configuration.json, indicator_hits.csv, file_hash_hits.json, preparation_manifest.json and evidence.sqlite. Prepared input copies remain in inputs/. Preparation-manifest paths are relative to the result root. Decoding details retain originals, transformations and errors. FINDINGS.csv prefixes spreadsheet-sensitive text with an apostrophe; exact values remain in JSON. Authorized-test/context/reference hits are explicitly labelled. Direct engine.scan calls and failed/interrupted CLI scans retain flat exports. 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.txt and details/summary.json. If the scan failed before presentation, inspect summary.json at the result root instead. 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.