prem / lib /kavacha /layers /layer5_payload_analyzer.dart
Nitishkumar-ai's picture
Deploy source code to Hugging Face without binaries
c25dcd7
import 'dart:io';
import 'dart:math';
import '../models/kavacha_models.dart';
class Layer5PayloadAnalyzer implements KavachaLayer {
static const List<String> officialDomains = [
'sbi.co.in',
'onlinesbi.sbi',
'hdfcbank.com',
'icicibank.com',
'axisbank.com',
'incometax.gov.in',
'indiapost.gov.in',
'pnbindia.in',
'epfindia.gov.in',
'irctc.co.in'
];
@override
Future<LayerResult> inspect(SmsContext ctx) async {
final urls = _extractUrls(ctx.body);
if (urls.isEmpty) {
return LayerResult.pass();
}
for (String url in urls) {
final domain = _extractDomain(url);
if (domain.isEmpty) continue;
// 1. Squatter Check
for (String official in officialDomains) {
if (domain == official || domain.endsWith('.\$official')) {
// Exact match, trusted domain
continue;
}
int distance = _levenshtein(domain, official);
// If it's suspiciously close (distance 1 or 2) but NOT an exact match
if (distance > 0 && distance <= 2) {
return LayerResult.block(
"Domain Squatting Detected", 0.98, ["payload_squatter:\$domain(sim:\$official)"], 5);
}
}
// 2. Payload Check
bool isApk = await _isApkPayload(url);
if (isApk) {
return LayerResult.block("Malicious APK Payload", 0.99, ["payload_apk:\$url"], 5);
}
}
return LayerResult.pass();
}
List<String> _extractUrls(String text) {
RegExp exp = RegExp(
r"https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)",
caseSensitive: false);
return exp.allMatches(text).map((m) => m.group(0)!).toList();
}
String _extractDomain(String urlString) {
try {
return Uri.parse(urlString).host.toLowerCase();
} catch (e) {
return "";
}
}
Future<bool> _isApkPayload(String urlString) async {
if (urlString.toLowerCase().endsWith('.apk')) return true;
// Attempt lightweight HEAD request for hidden payloads
try {
final client = HttpClient();
client.connectionTimeout = const Duration(seconds: 3);
final request = await client.headUrl(Uri.parse(urlString));
request.followRedirects = true;
request.maxRedirects = 3;
final response = await request.close();
final contentType = response.headers.value(HttpHeaders.contentTypeHeader);
if (contentType != null && contentType.contains('application/vnd.android.package-archive')) {
return true;
}
} catch (e) {
// Ignore network timeouts silently
}
return false;
}
int _levenshtein(String a, String b) {
if (a.isEmpty) return b.length;
if (b.isEmpty) return a.length;
List<int> costs = List.generate(b.length + 1, (i) => i);
for (int i = 0; i < a.length; i++) {
int lastValue = i + 1;
for (int j = 0; j < b.length; j++) {
int newValue = costs[j] + (a[i] == b[j] ? 0 : 1);
if (newValue > lastValue + 1) newValue = lastValue + 1;
if (newValue > costs[j + 1] + 1) newValue = costs[j + 1] + 1;
costs[j] = lastValue;
lastValue = newValue;
}
costs[b.length] = lastValue;
}
return costs[b.length];
}
}