reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Safe sample-handling helpers: hashing, provenance records, and IOC defanging.
|
||||
|
||||
Operates only on file bytes and text — it never executes a sample. Use it to compute a
|
||||
sample's identity, emit a provenance record, and defang indicators before they leave the lab.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as _dt
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_DEFANG_SCHEME = re.compile(r"\bhttp(s?)://", re.I)
|
||||
_DEFANG_DOT = re.compile(r"\.(?=[A-Za-z0-9])")
|
||||
_IPV4 = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
|
||||
|
||||
|
||||
def compute_hashes(filepath: str | Path) -> dict:
|
||||
data = Path(filepath).read_bytes()
|
||||
return {
|
||||
"md5": hashlib.md5(data).hexdigest(), # noqa: S324 - identification only
|
||||
"sha1": hashlib.sha1(data).hexdigest(), # noqa: S324
|
||||
"sha256": hashlib.sha256(data).hexdigest(),
|
||||
"size": len(data),
|
||||
}
|
||||
|
||||
|
||||
def make_record(filepath: str | Path, source: str, case: str) -> dict:
|
||||
rec = {
|
||||
"case": case,
|
||||
"source": source,
|
||||
"filename": Path(filepath).name,
|
||||
"recorded_at": _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
}
|
||||
rec.update(compute_hashes(filepath))
|
||||
return rec
|
||||
|
||||
|
||||
def defang(text: str) -> str:
|
||||
"""Render URLs/IPs non-clickable for safe sharing."""
|
||||
def _ip(m: re.Match) -> str:
|
||||
return m.group(0).replace(".", "[.]")
|
||||
text = _IPV4.sub(_ip, text)
|
||||
text = _DEFANG_SCHEME.sub(lambda m: "hxxp" + ("s" if m.group(1) else "") + "://", text)
|
||||
# bracket the dot in domains within URLs only (lines containing hxxp)
|
||||
out_lines = []
|
||||
for line in text.splitlines():
|
||||
if "hxxp" in line:
|
||||
line = _DEFANG_DOT.sub("[.]", line)
|
||||
out_lines.append(line)
|
||||
return "\n".join(out_lines)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Safe sample-handling helpers")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
p_hash = sub.add_parser("hash", help="compute file hashes")
|
||||
p_hash.add_argument("file")
|
||||
|
||||
p_rec = sub.add_parser("record", help="emit a provenance JSON record")
|
||||
p_rec.add_argument("file")
|
||||
p_rec.add_argument("--source", required=True)
|
||||
p_rec.add_argument("--case", required=True)
|
||||
|
||||
p_def = sub.add_parser("defang", help="defang URLs/IPs in a text file")
|
||||
p_def.add_argument("file")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.cmd == "hash":
|
||||
print(json.dumps(compute_hashes(args.file), indent=2))
|
||||
elif args.cmd == "record":
|
||||
print(json.dumps(make_record(args.file, args.source, args.case), indent=2))
|
||||
elif args.cmd == "defang":
|
||||
print(defang(Path(args.file).read_text(encoding="utf-8", errors="replace")))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user