Skip to content

Instantly share code, notes, and snippets.

@birkin
Created April 20, 2026 16:30
Show Gist options
  • Select an option

  • Save birkin/72f01455668aca535209dc16af51840b to your computer and use it in GitHub Desktop.

Select an option

Save birkin/72f01455668aca535209dc16af51840b to your computer and use it in GitHub Desktop.
sanitizes mod-security output for LLM analysis.
#!/usr/bin/env -S uv run
"""
Sanitizes extracted mod_security log output for safer sharing.
This script preserves the general structure and diagnostic meaning of the log
while redacting likely sensitive values such as IP addresses, hostnames, URIs,
unique request identifiers, timestamps, and absolute file paths.
Usage:
uv run ./sanitize_mod_sec_logs.py
uv run ./sanitize_mod_sec_logs.py ./extracted_modsec_log.txt
uv run ./sanitize_mod_sec_logs.py ./extracted_modsec_log.txt -o ./sanitized_extracted_modsec_log.txt
"""
import argparse
import re
from pathlib import Path
UNIQUE_ID_PATTERN = re.compile(r'\b(?=[A-Za-z0-9_-]{20,}\b)(?=.*\d)(?=.*[a-z])[A-Za-z0-9-]+\b')
def sanitize_line(line: str) -> str:
"""
Sanitizes one mod_security log line by obfuscating sensitive values.
Called by: sanitize_text()
"""
sanitized_line = line
sanitized_line = re.sub(r'\[file "/[^"]+"\]', '[file "<file-path>"]', sanitized_line)
## replaces bracketed key-value fields that commonly carry sensitive details
bracket_patterns: list[tuple[str, str]] = [
(r'\[client [^\]]+\]', '[client <client-ip>]'),
(r'\[hostname "[^"]+"\]', '[hostname "<hostname>"]'),
(r'\[uri "[^"]+"\]', '[uri "<uri>"]'),
(r'\[unique_id "[^"]+"\]', '[unique_id "<unique-id>"]'),
]
for pattern, replacement in bracket_patterns:
sanitized_line = re.sub(pattern, replacement, sanitized_line)
## replaces standalone timestamped request-header lines and raw paths
sanitized_line = re.sub(
r'^\[(?P<timestamp>[^\]]+)\]\s+\S+\s+\d{1,3}(?:\.\d{1,3}){3}\s+\d+\s+\d{1,3}(?:\.\d{1,3}){3}\s+\d+$',
'[<timestamp>] <unique-id> <client-ip> <client-port> <server-ip> <server-port>',
sanitized_line,
)
sanitized_line = re.sub(r'/etc/[\w./-]+', '<file-path>', sanitized_line)
sanitized_line = UNIQUE_ID_PATTERN.sub('<unique-id>', sanitized_line)
## replaces remaining identifiers, addresses, and timing values
replacements: list[tuple[str, str]] = [
(
r'\b\d{1,3}(?:\.\d{1,3}){3}\b',
'<ip-address>',
),
(
r'^Stopwatch:\s+.+$',
'Stopwatch: <timing-redacted>',
),
(
r'^Stopwatch2:\s+.+$',
'Stopwatch2: <timing-redacted>',
),
]
for pattern, replacement in replacements:
sanitized_line = re.sub(pattern, replacement, sanitized_line)
return sanitized_line
def sanitize_text(text: str) -> str:
"""
Sanitizes the full mod_security log text while preserving general structure.
Called by: write_sanitized_file()
"""
sanitized_lines: list[str] = []
for line in text.splitlines():
sanitized_lines.append(sanitize_line(line))
sanitized_text = '\n'.join(sanitized_lines)
if text.endswith('\n'):
sanitized_text = f'{sanitized_text}\n'
return sanitized_text
def write_sanitized_file(input_path: Path, output_path: Path) -> None:
"""
Reads the input file, sanitizes it, and writes the sanitized output file.
Called by: main()
"""
original_text = input_path.read_text(encoding='utf-8')
sanitized_text = sanitize_text(original_text)
output_path.write_text(sanitized_text, encoding='utf-8')
def build_argument_parser() -> argparse.ArgumentParser:
"""
Builds the CLI argument parser for the sanitizer script.
Called by: main()
"""
parser = argparse.ArgumentParser(
description='Sanitize extracted mod_security logs for safe sharing with an LLM.',
)
parser.add_argument(
'input_file',
nargs='?',
default='extracted_modsec_log.txt',
help='Path to the input mod_security log file.',
)
parser.add_argument(
'-o',
'--output',
default='sanitized_extracted_modsec_log.txt',
help='Path to write the sanitized output file.',
)
return parser
def main() -> None:
"""
Parses arguments and runs the sanitizer.
Called by: __main__
"""
parser = build_argument_parser()
args = parser.parse_args()
input_path = Path(args.input_file)
output_path = Path(args.output)
write_sanitized_file(input_path, output_path)
print(f'Sanitized log written to {output_path}')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment