Skip to content

Commit ebbeb0a

Browse files
NullSec8cursoragent
andcommitted
Strengthen privacy engine heuristics and scoring
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6da1c0a commit ebbeb0a

2 files changed

Lines changed: 146 additions & 38 deletions

File tree

PrivacyEngine.cs

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -921,16 +921,18 @@ private static void DetectHighEntropyParams(RequestEntry req, List<DetectionSign
921921
double entropy = ShannonEntropy(val);
922922
bool isBase64 = Base64Payload.IsMatch(val);
923923
bool isHex = HexPayload.IsMatch(val);
924+
bool hasEntropyPattern = HighEntropyPattern.IsMatch(val);
924925

925-
if (entropy > 4.0 || isBase64 || isHex)
926+
if (entropy > 3.8 || isBase64 || isHex || hasEntropyPattern)
926927
{
927-
double conf = entropy > 4.5 ? 0.85 : entropy > 4.0 ? 0.70 : 0.55;
928-
if (isBase64 || isHex) conf = Math.Max(conf, 0.75);
928+
double conf = entropy > 4.5 ? 0.85 : entropy > 4.0 ? 0.75 : 0.60;
929+
if (isBase64 || isHex || hasEntropyPattern) conf = Math.Max(conf, 0.80);
929930
signals.Add(new DetectionSignal
930931
{
931932
SignalType = "high_entropy_param",
932933
Source = req.Host,
933-
Detail = $"Param '{parts[0]}' has high entropy ({entropy:F1}) — possible obfuscated tracking ID" + (isBase64 ? " [Base64]" : "") + (isHex ? " [Hex]" : ""),
934+
Detail = $"Param '{parts[0]}' has high-entropy identifier ({entropy:F1}) — possible obfuscated tracking ID"
935+
+ (isBase64 ? " [Base64]" : "") + (isHex ? " [Hex]" : "") + (hasEntropyPattern ? " [Random-looking]" : ""),
934936
Confidence = conf,
935937
Risk = RiskType.Tracking,
936938
Severity = 3,
@@ -1046,16 +1048,24 @@ private static void DetectCookieSync(RequestEntry req, List<DetectionSignal> sig
10461048
req.Path.Contains("sync", StringComparison.OrdinalIgnoreCase) ||
10471049
req.Path.Contains("match", StringComparison.OrdinalIgnoreCase);
10481050

1049-
if (hasIds)
1051+
bool highRiskTracker =
1052+
string.Equals(req.TrackerCategoryName, "Advertising", StringComparison.OrdinalIgnoreCase) ||
1053+
string.Equals(req.TrackerCategoryName, "DMP", StringComparison.OrdinalIgnoreCase) ||
1054+
string.Equals(req.TrackerCategoryName, "Attribution", StringComparison.OrdinalIgnoreCase);
1055+
1056+
if (hasIds || highRiskTracker)
10501057
{
1058+
double confidence = highRiskTracker ? 0.80 : 0.70;
1059+
int severity = highRiskTracker ? 5 : 4;
1060+
10511061
signals.Add(new DetectionSignal
10521062
{
10531063
SignalType = "cookie_sync",
10541064
Source = req.Host,
10551065
Detail = "Cookie sync detected — identifiers passed to third-party ad-tech",
1056-
Confidence = 0.70,
1066+
Confidence = confidence,
10571067
Risk = RiskType.Tracking,
1058-
Severity = 4,
1068+
Severity = severity,
10591069
Evidence = $"Cookie header present with tracking params to {req.Host}",
10601070
GdprArticle = "Art. 5(1)(a)"
10611071
});
@@ -1098,6 +1108,19 @@ public static string ClassifyCookie(string name)
10981108
var lower = name.ToLowerInvariant();
10991109
if (TrackingCookiePatterns.Any(p => lower.StartsWith(p.ToLowerInvariant()) || lower == p.ToLowerInvariant()))
11001110
return "Tracking / Analytics";
1111+
1112+
// Stronger heuristic: generic identifier-style cookies used by many tracking stacks
1113+
if (lower.Contains("visitorid") || lower.Contains("visitor_id") ||
1114+
lower.Contains("deviceid") || lower.Contains("device_id") ||
1115+
lower.Contains("userid") || lower.Contains("user_id") ||
1116+
lower.Contains("clientid") || lower.Contains("client_id") ||
1117+
lower.Contains("customerid") || lower.Contains("customer_id") ||
1118+
lower.Contains("browserid") || lower.Contains("browser_id") ||
1119+
lower.Contains("uuid"))
1120+
{
1121+
return "Tracking / Analytics";
1122+
}
1123+
11011124
if (lower.Contains("session") || lower.Contains("sid") || lower.Contains("csrf") || lower.Contains("token") || lower.Contains("auth"))
11021125
return "Session / Security";
11031126
if (lower.Contains("consent") || lower.Contains("gdpr") || lower.Contains("ccpa") || lower.Contains("cookie") || lower.Contains("optanon"))
@@ -1112,6 +1135,19 @@ public static string ClassifyStorageKey(string key)
11121135
var lower = key.ToLowerInvariant();
11131136
if (TrackingCookiePatterns.Any(p => lower.Contains(p.ToLowerInvariant())))
11141137
return "Tracking / Analytics";
1138+
1139+
// Stronger heuristic: identifier-style keys commonly used for tracking
1140+
if (lower.Contains("visitorid") || lower.Contains("visitor_id") ||
1141+
lower.Contains("deviceid") || lower.Contains("device_id") ||
1142+
lower.Contains("userid") || lower.Contains("user_id") ||
1143+
lower.Contains("clientid") || lower.Contains("client_id") ||
1144+
lower.Contains("customerid") || lower.Contains("customer_id") ||
1145+
lower.Contains("browserid") || lower.Contains("browser_id") ||
1146+
lower.Contains("uuid"))
1147+
{
1148+
return "Tracking / Analytics";
1149+
}
1150+
11151151
if (lower.Contains("token") || lower.Contains("auth") || lower.Contains("session"))
11161152
return "Authentication";
11171153
if (lower.Contains("cache") || lower.Contains("sw-") || lower.Contains("workbox"))
@@ -1153,6 +1189,24 @@ public static List<string> ClassifyRequestData(RequestEntry req)
11531189
var url = req.FullUrl.ToLowerInvariant();
11541190
if (url.Contains("geo") || url.Contains("location") || url.Contains("lat=") || url.Contains("lng=")) tags.Add("Location data");
11551191
if (url.Contains("email") || url.Contains("phone") || url.Contains("name=") || url.Contains("address")) tags.Add("Possible PII");
1192+
1193+
if (req.IsThirdParty) tags.Add("Shared with other companies");
1194+
1195+
if (!string.IsNullOrEmpty(req.TrackerCategoryName))
1196+
{
1197+
if (string.Equals(req.TrackerCategoryName, "Advertising", StringComparison.OrdinalIgnoreCase) ||
1198+
string.Equals(req.TrackerCategoryName, "DMP", StringComparison.OrdinalIgnoreCase) ||
1199+
string.Equals(req.TrackerCategoryName, "Attribution", StringComparison.OrdinalIgnoreCase))
1200+
{
1201+
tags.Add("Ad-tech / profiling");
1202+
}
1203+
else if (string.Equals(req.TrackerCategoryName, "Analytics", StringComparison.OrdinalIgnoreCase) ||
1204+
string.Equals(req.TrackerCategoryName, "SessionReplay", StringComparison.OrdinalIgnoreCase))
1205+
{
1206+
tags.Add("Behavioral analytics");
1207+
}
1208+
}
1209+
11561210
return tags;
11571211
}
11581212

@@ -1288,17 +1342,17 @@ public static List<GdprFinding> MapToGdpr(ScanResult scan)
12881342

12891343
private static double BaselineWeight(TrackerCategory cat) => cat switch
12901344
{
1291-
TrackerCategory.CMP => 0.2, // Consent tools are infrastructure, very low risk
1345+
TrackerCategory.CMP => 0.3, // Consent tools are infrastructure, but still expose choices
12921346
TrackerCategory.CDN => 0.1, // CDN is expected
1293-
TrackerCategory.AdVerification => 0.5, // Verification is semi-expected
1294-
TrackerCategory.Affiliate => 0.6, // Affiliate tracking is common
1295-
TrackerCategory.Analytics => 0.7, // Standard analytics — normal web behavior
1296-
TrackerCategory.Social => 0.8, // Social widgets carry moderate risk
1297-
TrackerCategory.Attribution => 0.9, // Attribution is aggressive mobile tracking
1298-
TrackerCategory.Advertising => 1.0, // Full penalty for ad trackers
1299-
TrackerCategory.SessionReplay => 1.3, // Session replay is invasive — amplified penalty
1300-
TrackerCategory.DMP => 1.5, // Data brokers are highest risk — amplified
1301-
TrackerCategory.Fingerprinting => 1.4, // Fingerprinting evades consent — amplified
1347+
TrackerCategory.AdVerification => 0.6, // Verification is semi-expected
1348+
TrackerCategory.Affiliate => 0.7, // Affiliate tracking is common
1349+
TrackerCategory.Analytics => 0.8, // Standard analytics — normal web behavior but still tracking
1350+
TrackerCategory.Social => 0.9, // Social widgets carry moderate-to-high risk
1351+
TrackerCategory.Attribution => 1.0, // Attribution is aggressive mobile tracking
1352+
TrackerCategory.Advertising => 1.1, // Slightly stronger penalty for ad trackers
1353+
TrackerCategory.SessionReplay => 1.5, // Session replay is invasive — amplified penalty
1354+
TrackerCategory.DMP => 1.8, // Data brokers are highest risk — amplified further
1355+
TrackerCategory.Fingerprinting => 1.6, // Fingerprinting evades consent — amplified
13021356
_ => 1.0
13031357
};
13041358

@@ -1330,7 +1384,7 @@ public static PrivacyScore CalculateScore(ScanResult scan)
13301384
breakdown["Third-party domains"] = -domainPenalty;
13311385

13321386
// ── Fingerprinting: steep penalty ──
1333-
int fpPenalty = Math.Min(35, scan.Fingerprints.Count * 10);
1387+
int fpPenalty = Math.Min(40, scan.Fingerprints.Count * 12);
13341388
score -= fpPenalty; catScores["Fingerprinting"] -= fpPenalty;
13351389
breakdown["Fingerprinting"] = -fpPenalty;
13361390

@@ -1375,7 +1429,7 @@ public static PrivacyScore CalculateScore(ScanResult scan)
13751429
f.Type.StartsWith("Behavioral:") || f.Type.Contains("Session Replay") ||
13761430
f.Type.Contains("Obfuscation") || f.Type.Contains("Dynamic Script") ||
13771431
f.Type.Contains("Beacon Data") || f.Type.Contains("Cross-Frame"));
1378-
int behavioralPenalty = Math.Min(25, behavioralCount * 6);
1432+
int behavioralPenalty = Math.Min(25, behavioralCount * 7);
13791433
score -= behavioralPenalty; catScores["Behavioral"] -= behavioralPenalty;
13801434
if (behavioralPenalty > 0) breakdown["Behavioral tracking"] = -behavioralPenalty;
13811435

0 commit comments

Comments
 (0)