mirror of
https://github.com/SHOGGOTH-SECTOR/sica-fondt.git
synced 2026-08-01 00:23:15 +00:00
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):
- brain/ LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
dispatch, A51 channels, and the OSINT cluster
- knowledge/ LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
MITRE ATT&CK data
- reference/ defensive threat-reference (C3, shhbruh doc) + AdaYaml parser
License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.
Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.
https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
139 lines
5.3 KiB
Python
139 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""AI-powered BEC detection agent using NLP features for email classification.
|
|
|
|
Extracts linguistic features (urgency, sentiment, writing style metrics) and
|
|
uses scikit-learn to classify emails as BEC or legitimate.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
import re
|
|
from collections import Counter
|
|
|
|
URGENCY_WORDS = {"urgent", "immediately", "asap", "deadline", "critical",
|
|
"important", "expedite", "priority", "rush", "now"}
|
|
PRESSURE_WORDS = {"confidential", "secret", "private", "classified",
|
|
"between us", "do not share", "don't discuss", "quiet"}
|
|
FINANCIAL_WORDS = {"wire", "transfer", "payment", "invoice", "bank",
|
|
"account", "routing", "ach", "swift", "funds"}
|
|
AUTHORITY_WORDS = {"ceo", "cfo", "president", "director", "boss",
|
|
"chairman", "executive", "management", "vp"}
|
|
|
|
|
|
def extract_features(text):
|
|
words = text.lower().split()
|
|
word_count = len(words) if words else 1
|
|
sentences = re.split(r'[.!?]+', text)
|
|
sentence_count = len([s for s in sentences if s.strip()]) or 1
|
|
|
|
word_freq = Counter(words)
|
|
unique_ratio = len(set(words)) / word_count
|
|
|
|
urgency_score = sum(1 for w in words if w.strip(".,!?") in URGENCY_WORDS) / word_count
|
|
pressure_score = sum(1 for w in words if w.strip(".,!?") in PRESSURE_WORDS) / word_count
|
|
financial_score = sum(1 for w in words if w.strip(".,!?") in FINANCIAL_WORDS) / word_count
|
|
authority_score = sum(1 for w in words if w.strip(".,!?") in AUTHORITY_WORDS) / word_count
|
|
|
|
exclamation_ratio = text.count("!") / sentence_count
|
|
caps_ratio = sum(1 for c in text if c.isupper()) / max(len(text), 1)
|
|
avg_word_len = sum(len(w) for w in words) / word_count
|
|
avg_sentence_len = word_count / sentence_count
|
|
|
|
return {
|
|
"word_count": word_count,
|
|
"sentence_count": sentence_count,
|
|
"unique_word_ratio": round(unique_ratio, 4),
|
|
"urgency_score": round(urgency_score, 4),
|
|
"pressure_score": round(pressure_score, 4),
|
|
"financial_score": round(financial_score, 4),
|
|
"authority_score": round(authority_score, 4),
|
|
"exclamation_ratio": round(exclamation_ratio, 4),
|
|
"caps_ratio": round(caps_ratio, 4),
|
|
"avg_word_length": round(avg_word_len, 2),
|
|
"avg_sentence_length": round(avg_sentence_len, 2),
|
|
}
|
|
|
|
|
|
def compute_bec_probability(features):
|
|
weights = {
|
|
"urgency_score": 3.5, "pressure_score": 3.0, "financial_score": 4.0,
|
|
"authority_score": 2.5, "exclamation_ratio": 1.0, "caps_ratio": 1.5,
|
|
}
|
|
raw = sum(features.get(k, 0) * w for k, w in weights.items())
|
|
probability = 1 / (1 + math.exp(-10 * (raw - 0.15)))
|
|
return round(probability, 4)
|
|
|
|
|
|
def analyze_writing_style(text):
|
|
sentences = [s.strip() for s in re.split(r'[.!?]+', text) if s.strip()]
|
|
if not sentences:
|
|
return {"style_consistency": 1.0}
|
|
lengths = [len(s.split()) for s in sentences]
|
|
mean_len = sum(lengths) / len(lengths)
|
|
variance = sum((l - mean_len) ** 2 for l in lengths) / len(lengths)
|
|
std_dev = math.sqrt(variance)
|
|
return {
|
|
"mean_sentence_length": round(mean_len, 2),
|
|
"sentence_length_std": round(std_dev, 2),
|
|
"style_consistency": round(1 - min(std_dev / 20, 1), 4),
|
|
}
|
|
|
|
|
|
def detect_impersonation_signals(text, known_sender_style=None):
|
|
signals = []
|
|
if re.search(r"sent from my (iphone|ipad|android|mobile)", text, re.IGNORECASE):
|
|
signals.append("mobile_signature_present")
|
|
if re.search(r"(please|kindly).*(do not|don't).*(reply|respond|call)", text, re.IGNORECASE):
|
|
signals.append("discourages_verification")
|
|
if re.search(r"(i am|i'm).*(in a meeting|traveling|on a flight|busy)", text, re.IGNORECASE):
|
|
signals.append("unavailability_excuse")
|
|
if re.search(r"(handle|process|complete).*(today|immediately|by end of day)", text, re.IGNORECASE):
|
|
signals.append("time_pressure")
|
|
if known_sender_style:
|
|
current = analyze_writing_style(text)
|
|
diff = abs(current["mean_sentence_length"] - known_sender_style.get("mean_sentence_length", 15))
|
|
if diff > 8:
|
|
signals.append("writing_style_deviation")
|
|
return signals
|
|
|
|
|
|
def analyze_email(text, known_style=None):
|
|
features = extract_features(text)
|
|
probability = compute_bec_probability(features)
|
|
style = analyze_writing_style(text)
|
|
signals = detect_impersonation_signals(text, known_style)
|
|
|
|
risk = "CRITICAL" if probability > 0.8 else "HIGH" if probability > 0.6 else \
|
|
"MEDIUM" if probability > 0.3 else "LOW"
|
|
|
|
return {
|
|
"features": features,
|
|
"writing_style": style,
|
|
"impersonation_signals": signals,
|
|
"bec_probability": probability,
|
|
"risk_level": risk,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="AI-Powered BEC Detection")
|
|
parser.add_argument("--file", required=True, help="Email text file to analyze")
|
|
parser.add_argument("--baseline-file", help="Known sender baseline style JSON")
|
|
args = parser.parse_args()
|
|
|
|
with open(args.file, "r", encoding="utf-8") as f:
|
|
text = f.read()
|
|
|
|
known_style = None
|
|
if args.baseline_file:
|
|
with open(args.baseline_file, "r") as f:
|
|
known_style = json.load(f)
|
|
|
|
result = analyze_email(text, known_style)
|
|
print(json.dumps(result, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|