160 lines
9.5 KiB
PHP
Executable File
160 lines
9.5 KiB
PHP
Executable File
<?php
|
|
header("Content-Type: application/json");
|
|
header("Access-Control-Allow-Origin: *");
|
|
$db = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$action = $_GET["action"] ?? $_POST["action"] ?? "status";
|
|
|
|
// ISP filter rules engine
|
|
function analyzeTemplate($subject, $html, $fromDomain) {
|
|
$issues = []; $scores = ["gmail"=>100,"outlook"=>100,"yahoo"=>100,"gmx"=>100];
|
|
|
|
// 1. Spam trigger words
|
|
$spamWords = ["free","winner","congratulations","click here","act now","limited time","buy now","cheap","discount","offer expires","100%","guarantee","no obligation","risk free","urgent","casino","viagra","crypto","bitcoin","earn money","work from home","lose weight"];
|
|
$subjectLower = strtolower($subject); $htmlLower = strtolower(strip_tags($html));
|
|
$found = [];
|
|
foreach($spamWords as $w) {
|
|
if(strpos($subjectLower, $w) !== false || strpos($htmlLower, $w) !== false) $found[] = $w;
|
|
}
|
|
if(count($found) > 0) {
|
|
$penalty = min(count($found) * 5, 40);
|
|
foreach($scores as $k=>&$v) $v -= $penalty;
|
|
$issues[] = ["severity"=>"high","rule"=>"spam_words","detail"=>"Found ".count($found)." spam triggers: ".implode(", ",$found),"penalty"=>$penalty];
|
|
}
|
|
|
|
// 2. HTML ratio
|
|
$textLen = strlen(strip_tags($html)); $htmlLen = strlen($html);
|
|
$ratio = $htmlLen > 0 ? $textLen / $htmlLen : 0;
|
|
if($ratio < 0.2) {
|
|
foreach($scores as $k=>&$v) $v -= 15;
|
|
$issues[] = ["severity"=>"medium","rule"=>"html_ratio","detail"=>"Text/HTML ratio too low: ".round($ratio*100)."%","penalty"=>15];
|
|
}
|
|
|
|
// 3. Too many links
|
|
$linkCount = substr_count(strtolower($html), '<a ') + substr_count(strtolower($html), 'href=');
|
|
if($linkCount > 5) {
|
|
$p = min(($linkCount - 5) * 3, 20);
|
|
foreach($scores as $k=>&$v) $v -= $p;
|
|
$issues[] = ["severity"=>"medium","rule"=>"too_many_links","detail"=>"$linkCount links found (max recommended: 5)","penalty"=>$p];
|
|
}
|
|
|
|
// 4. Image-only email
|
|
$imgCount = substr_count(strtolower($html), '<img');
|
|
if($imgCount > 0 && $textLen < 100) {
|
|
foreach($scores as $k=>&$v) $v -= 25;
|
|
$issues[] = ["severity"=>"high","rule"=>"image_only","detail"=>"Image-heavy email with $imgCount images and only $textLen chars of text","penalty"=>25];
|
|
}
|
|
|
|
// 5. Subject line issues
|
|
if(strlen($subject) > 80) {
|
|
$scores["gmail"] -= 5; $scores["outlook"] -= 3;
|
|
$issues[] = ["severity"=>"low","rule"=>"subject_long","detail"=>"Subject too long: ".strlen($subject)." chars","penalty"=>5];
|
|
}
|
|
if(preg_match('/[A-Z]{5,}/', $subject)) {
|
|
foreach($scores as $k=>&$v) $v -= 10;
|
|
$issues[] = ["severity"=>"medium","rule"=>"caps_subject","detail"=>"Excessive caps in subject","penalty"=>10];
|
|
}
|
|
if(preg_match('/[!]{2,}|[$]{2,}|[*]{3,}/', $subject)) {
|
|
foreach($scores as $k=>&$v) $v -= 8;
|
|
$issues[] = ["severity"=>"medium","rule"=>"special_chars","detail"=>"Excessive special characters in subject","penalty"=>8];
|
|
}
|
|
|
|
// 6. Missing unsubscribe
|
|
if(stripos($html, 'unsubscribe') === false && stripos($html, 'désabonner') === false) {
|
|
$scores["gmail"] -= 15; $scores["outlook"] -= 10; $scores["yahoo"] -= 10;
|
|
$issues[] = ["severity"=>"high","rule"=>"no_unsubscribe","detail"=>"No unsubscribe link found","penalty"=>15];
|
|
}
|
|
|
|
// 7. Domain reputation check
|
|
if(strpos($fromDomain, '.charity') !== false || strpos($fromDomain, '.xyz') !== false || strpos($fromDomain, '.top') !== false) {
|
|
foreach($scores as $k=>&$v) $v -= 10;
|
|
$issues[] = ["severity"=>"medium","rule"=>"suspicious_tld","detail"=>"TLD '$fromDomain' has lower trust","penalty"=>10];
|
|
}
|
|
|
|
// 8. JavaScript detection
|
|
if(stripos($html, '<script') !== false || stripos($html, 'javascript:') !== false) {
|
|
foreach($scores as $k=>&$v) $v -= 30;
|
|
$issues[] = ["severity"=>"critical","rule"=>"javascript","detail"=>"JavaScript detected - will be blocked by all ISPs","penalty"=>30];
|
|
}
|
|
|
|
// 9. Base64 encoded content
|
|
if(preg_match('/base64[,;]/', $html)) {
|
|
$scores["gmail"] -= 15; $scores["outlook"] -= 20;
|
|
$issues[] = ["severity"=>"medium","rule"=>"base64_content","detail"=>"Base64 encoded content detected","penalty"=>15];
|
|
}
|
|
|
|
// 10. URL shorteners
|
|
$shorteners = ["bit.ly","tinyurl","goo.gl","t.co","ow.ly","is.gd","buff.ly"];
|
|
foreach($shorteners as $s) {
|
|
if(stripos($html, $s) !== false) {
|
|
foreach($scores as $k=>&$v) $v -= 20;
|
|
$issues[] = ["severity"=>"high","rule"=>"url_shortener","detail"=>"URL shortener '$s' detected - major spam signal","penalty"=>20];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Clamp scores
|
|
foreach($scores as $k=>&$v) $v = max(0, min(100, $v));
|
|
$overall = round(array_sum($scores) / count($scores));
|
|
|
|
$recommendations = [];
|
|
if($overall < 60) $recommendations[] = "CRITICAL: Template needs major revision before sending";
|
|
if($overall < 80) $recommendations[] = "Consider A/B testing with a small segment first";
|
|
foreach($issues as $i) {
|
|
if($i["severity"] == "critical") $recommendations[] = "FIX IMMEDIATELY: ".$i["detail"];
|
|
elseif($i["severity"] == "high") $recommendations[] = "Fix: ".$i["detail"];
|
|
}
|
|
if(empty($recommendations)) $recommendations[] = "Template looks good for sending";
|
|
|
|
return ["scores"=>$scores,"overall"=>$overall,"issues"=>$issues,"recommendations"=>$recommendations,"grade"=>$overall>=90?"A":($overall>=75?"B":($overall>=60?"C":($overall>=40?"D":"F")))];
|
|
}
|
|
|
|
try {
|
|
switch($action) {
|
|
case "status":
|
|
$total = $db->query("SELECT count(*) FROM admin.adversarial_tests")->fetchColumn();
|
|
echo json_encode(["status"=>"success","service"=>"adversarial-sandbox","tests_run"=>$total,"rules_active"=>10,"isps_simulated"=>["gmail","outlook","yahoo","gmx"],"actions"=>["status","test","history","stats","rules"]]);
|
|
break;
|
|
case "test":
|
|
$d = json_decode(file_get_contents("php://input"), true) ?: $_POST;
|
|
$subject = $d["subject"] ?? "Test Subject";
|
|
$html = $d["html"] ?? $d["body"] ?? "<p>Test email body</p>";
|
|
$from = $d["from_domain"] ?? "culturellemejean.charity";
|
|
$result = analyzeTemplate($subject, $html, $from);
|
|
// Save to DB
|
|
$stmt = $db->prepare("INSERT INTO admin.adversarial_tests (template_name,subject,html_content,test_results,gmail_score,outlook_score,yahoo_score,overall_score,issues,recommendations) VALUES (?,?,?,?,?,?,?,?,?,?) RETURNING id");
|
|
$stmt->execute([$d["name"]??"unnamed",$subject,$html,json_encode($result),$result["scores"]["gmail"],$result["scores"]["outlook"],$result["scores"]["yahoo"],$result["overall"],json_encode($result["issues"]),implode("\n",$result["recommendations"])]);
|
|
$id = $stmt->fetch(PDO::FETCH_ASSOC)["id"];
|
|
$result["test_id"] = $id;
|
|
$result["status"] = "success";
|
|
echo json_encode($result);
|
|
break;
|
|
case "history":
|
|
$rows = $db->query("SELECT id,template_name,subject,gmail_score,outlook_score,yahoo_score,overall_score,created_at FROM admin.adversarial_tests ORDER BY created_at DESC LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
|
|
echo json_encode(["status"=>"success","data"=>$rows]);
|
|
break;
|
|
case "stats":
|
|
$avg = $db->query("SELECT ROUND(AVG(gmail_score),1) as gmail, ROUND(AVG(outlook_score),1) as outlook, ROUND(AVG(yahoo_score),1) as yahoo, ROUND(AVG(overall_score),1) as overall, count(*) as total FROM admin.adversarial_tests")->fetch(PDO::FETCH_ASSOC);
|
|
$best = $db->query("SELECT template_name, overall_score FROM admin.adversarial_tests ORDER BY overall_score DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
|
|
$worst = $db->query("SELECT template_name, overall_score FROM admin.adversarial_tests ORDER BY overall_score ASC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC);
|
|
echo json_encode(["status"=>"success","averages"=>$avg,"best"=>$best,"worst"=>$worst]);
|
|
break;
|
|
case "rules":
|
|
echo json_encode(["status"=>"success","rules"=>[
|
|
["id"=>1,"name"=>"spam_words","description"=>"Detects common spam trigger words in subject and body","severity"=>"high","penalty"=>"5 per word, max 40"],
|
|
["id"=>2,"name"=>"html_ratio","description"=>"Checks text/HTML ratio (min 20%)","severity"=>"medium","penalty"=>"15"],
|
|
["id"=>3,"name"=>"too_many_links","description"=>"Flags emails with more than 5 links","severity"=>"medium","penalty"=>"3 per extra link"],
|
|
["id"=>4,"name"=>"image_only","description"=>"Detects image-heavy emails with little text","severity"=>"high","penalty"=>"25"],
|
|
["id"=>5,"name"=>"subject_length","description"=>"Subject line over 80 chars","severity"=>"low","penalty"=>"5"],
|
|
["id"=>6,"name"=>"caps_subject","description"=>"Excessive capitals in subject","severity"=>"medium","penalty"=>"10"],
|
|
["id"=>7,"name"=>"no_unsubscribe","description"=>"Missing unsubscribe/désabonner link","severity"=>"high","penalty"=>"15"],
|
|
["id"=>8,"name"=>"suspicious_tld","description"=>"Low-trust TLDs (.charity, .xyz, .top)","severity"=>"medium","penalty"=>"10"],
|
|
["id"=>9,"name"=>"javascript","description"=>"JavaScript in email body","severity"=>"critical","penalty"=>"30"],
|
|
["id"=>10,"name"=>"url_shortener","description"=>"URL shorteners (bit.ly, etc)","severity"=>"high","penalty"=>"20"]
|
|
]]);
|
|
break;
|
|
default:
|
|
echo json_encode(["error"=>"Unknown action"]);
|
|
}
|
|
} catch(Exception $e) { echo json_encode(["error"=>$e->getMessage()]); }
|