mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 21:54:56 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
222 lines
7.7 KiB
Python
222 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Log Integrity Chain Agent - Implements SHA-256 hash-chained append-only log for tamper detection."""
|
|
|
|
import json
|
|
import hashlib
|
|
import logging
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
GENESIS_HASH = "0" * 64
|
|
|
|
|
|
def compute_hash(data):
|
|
"""Compute SHA-256 hash of a string."""
|
|
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def create_chain_entry(index, timestamp, content, prev_hash):
|
|
"""Create a single chain entry with hash linking."""
|
|
content_hash = compute_hash(content)
|
|
chain_input = f"{prev_hash}{timestamp}{content_hash}"
|
|
chain_hash = compute_hash(chain_input)
|
|
return {
|
|
"index": index,
|
|
"timestamp": timestamp,
|
|
"content_hash": content_hash,
|
|
"prev_hash": prev_hash,
|
|
"chain_hash": chain_hash,
|
|
"content_preview": content[:200],
|
|
}
|
|
|
|
|
|
def load_chain(chain_file):
|
|
"""Load an existing hash chain from a JSON file."""
|
|
try:
|
|
with open(chain_file, "r") as f:
|
|
chain = json.load(f)
|
|
logger.info("Loaded chain with %d entries from %s", len(chain), chain_file)
|
|
return chain
|
|
except FileNotFoundError:
|
|
logger.info("No existing chain found, starting new chain")
|
|
return []
|
|
|
|
|
|
def save_chain(chain, chain_file):
|
|
"""Save the hash chain to a JSON file."""
|
|
with open(chain_file, "w") as f:
|
|
json.dump(chain, f, indent=2)
|
|
logger.info("Saved chain with %d entries to %s", len(chain), chain_file)
|
|
|
|
|
|
def ingest_log_file(log_file):
|
|
"""Read log entries from a file (one entry per line)."""
|
|
entries = []
|
|
with open(log_file, "r", errors="ignore") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line:
|
|
entries.append(line)
|
|
logger.info("Read %d log entries from %s", len(entries), log_file)
|
|
return entries
|
|
|
|
|
|
def ingest_json_log(json_file):
|
|
"""Read structured log entries from a JSON array file."""
|
|
with open(json_file, "r") as f:
|
|
data = json.load(f)
|
|
entries = []
|
|
if isinstance(data, list):
|
|
for item in data:
|
|
entries.append(json.dumps(item, sort_keys=True))
|
|
logger.info("Read %d JSON log entries from %s", len(entries), json_file)
|
|
return entries
|
|
|
|
|
|
def append_entries(chain, log_entries):
|
|
"""Append new log entries to the hash chain."""
|
|
prev_hash = chain[-1]["chain_hash"] if chain else GENESIS_HASH
|
|
start_index = len(chain)
|
|
new_entries = []
|
|
for i, content in enumerate(log_entries):
|
|
timestamp = datetime.utcnow().isoformat() + "Z"
|
|
entry = create_chain_entry(start_index + i, timestamp, content, prev_hash)
|
|
chain.append(entry)
|
|
new_entries.append(entry)
|
|
prev_hash = entry["chain_hash"]
|
|
logger.info("Appended %d entries to chain (total: %d)", len(new_entries), len(chain))
|
|
return new_entries
|
|
|
|
|
|
def verify_chain(chain):
|
|
"""Verify the integrity of the entire hash chain."""
|
|
if not chain:
|
|
return {"valid": True, "entries_checked": 0, "breaks": []}
|
|
breaks = []
|
|
prev_hash = GENESIS_HASH
|
|
for entry in chain:
|
|
expected_input = f"{prev_hash}{entry['timestamp']}{entry['content_hash']}"
|
|
expected_hash = compute_hash(expected_input)
|
|
if entry["chain_hash"] != expected_hash:
|
|
breaks.append({
|
|
"index": entry["index"],
|
|
"expected_hash": expected_hash,
|
|
"actual_hash": entry["chain_hash"],
|
|
"prev_hash_match": entry["prev_hash"] == prev_hash,
|
|
})
|
|
if entry["prev_hash"] != prev_hash:
|
|
breaks.append({
|
|
"index": entry["index"],
|
|
"issue": "prev_hash mismatch",
|
|
"expected_prev": prev_hash,
|
|
"actual_prev": entry["prev_hash"],
|
|
})
|
|
prev_hash = entry["chain_hash"]
|
|
valid = len(breaks) == 0
|
|
logger.info("Chain verification: %d entries checked, %d breaks found", len(chain), len(breaks))
|
|
return {"valid": valid, "entries_checked": len(chain), "breaks": breaks}
|
|
|
|
|
|
def find_tampered_range(breaks):
|
|
"""Identify the range of entries affected by tampering."""
|
|
if not breaks:
|
|
return None
|
|
first_break = min(b["index"] for b in breaks)
|
|
return {
|
|
"first_tampered_entry": first_break,
|
|
"total_affected": len(breaks),
|
|
"tamper_start_index": first_break,
|
|
"note": f"All entries from index {first_break} onward may be compromised",
|
|
}
|
|
|
|
|
|
def create_checkpoint(chain, checkpoint_file):
|
|
"""Create an integrity checkpoint with the current chain head hash."""
|
|
if not chain:
|
|
return None
|
|
checkpoint = {
|
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
"chain_length": len(chain),
|
|
"head_hash": chain[-1]["chain_hash"],
|
|
"head_index": chain[-1]["index"],
|
|
"genesis_hash": GENESIS_HASH,
|
|
"checkpoint_hash": compute_hash(f"{len(chain)}{chain[-1]['chain_hash']}"),
|
|
}
|
|
with open(checkpoint_file, "w") as f:
|
|
json.dump(checkpoint, f, indent=2)
|
|
logger.info("Created checkpoint at index %d: %s", checkpoint["head_index"], checkpoint["checkpoint_hash"][:16])
|
|
return checkpoint
|
|
|
|
|
|
def verify_checkpoint(chain, checkpoint_file):
|
|
"""Verify chain against a previously saved checkpoint."""
|
|
with open(checkpoint_file, "r") as f:
|
|
checkpoint = json.load(f)
|
|
cp_index = checkpoint["head_index"]
|
|
if cp_index >= len(chain):
|
|
return {"valid": False, "error": "Chain shorter than checkpoint"}
|
|
actual_hash = chain[cp_index]["chain_hash"]
|
|
valid = actual_hash == checkpoint["head_hash"]
|
|
return {
|
|
"valid": valid,
|
|
"checkpoint_index": cp_index,
|
|
"expected_hash": checkpoint["head_hash"],
|
|
"actual_hash": actual_hash,
|
|
}
|
|
|
|
|
|
def generate_report(verification, checkpoint, chain_length):
|
|
"""Generate log integrity verification report."""
|
|
report = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"chain_length": chain_length,
|
|
"integrity_valid": verification["valid"],
|
|
"entries_checked": verification["entries_checked"],
|
|
"breaks_found": len(verification["breaks"]),
|
|
"break_details": verification["breaks"][:20],
|
|
"checkpoint": checkpoint,
|
|
}
|
|
status = "INTACT" if verification["valid"] else "TAMPERED"
|
|
print(f"LOG INTEGRITY: {status} - {chain_length} entries, {len(verification['breaks'])} breaks")
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Log Integrity Chain Agent")
|
|
parser.add_argument("--log-file", help="Log file to ingest")
|
|
parser.add_argument("--chain-file", default="log_chain.json", help="Hash chain storage file")
|
|
parser.add_argument("--verify", action="store_true", help="Verify chain integrity")
|
|
parser.add_argument("--checkpoint", help="Create/verify checkpoint file")
|
|
parser.add_argument("--output", default="integrity_report.json")
|
|
args = parser.parse_args()
|
|
|
|
chain = load_chain(args.chain_file)
|
|
|
|
if args.log_file:
|
|
entries = ingest_log_file(args.log_file)
|
|
append_entries(chain, entries)
|
|
save_chain(chain, args.chain_file)
|
|
|
|
verification = {"valid": True, "entries_checked": 0, "breaks": []}
|
|
if args.verify:
|
|
verification = verify_chain(chain)
|
|
|
|
checkpoint = None
|
|
if args.checkpoint:
|
|
if args.verify:
|
|
checkpoint = verify_checkpoint(chain, args.checkpoint)
|
|
else:
|
|
checkpoint = create_checkpoint(chain, args.checkpoint)
|
|
|
|
report = generate_report(verification, checkpoint, len(chain))
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
logger.info("Report saved to %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|