Skip to content

Commit 8ab3219

Browse files
committed
update
1 parent 86c58ef commit 8ab3219

File tree

4 files changed

+239
-19
lines changed

4 files changed

+239
-19
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ wasm-bindgen-futures = { version = "0.4", optional = true }
1515
js-sys = { version = "0.3", optional = true }
1616

1717
bugreport_extractor_library = { git = "https://github.com/IsMyPhonePwned/bugreport-extractor-library.git", optional = true }
18+
sigma-zero = { git = "https://github.com/ping2A/sigmazero.git", optional = true }
19+
serde_yaml = { version = "0.9", optional = true }
1820

1921
# WebUSB support (optional)
2022
web-sys = { version = "0.3.83", optional = true, features = [
@@ -87,4 +89,4 @@ webusb = [
8789
"console_log",
8890
"zip",
8991
]
90-
bugreport-analysis = ["bugreport_extractor_library"]
92+
bugreport-analysis = ["bugreport_extractor_library", "sigma-zero", "serde_yaml"]

bugreport.html

Lines changed: 149 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,28 @@ <h2>Select Analysis Method</h2>
11771177

11781178
<!-- Results -->
11791179
<div id="results" class="results">
1180+
<!-- Pwned Results Detection (before Analysis Results) -->
1181+
<div class="section" id="detection-section">
1182+
<h2>🔍 Pwned Results Detection</h2>
1183+
<div class="subsection" style="margin-top: 0.75rem;">
1184+
<h4 style="font-size: 0.95rem; font-weight: 600; margin: 0 0 0.5rem 0; color: var(--text-secondary);">Sigma</h4>
1185+
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 0.5rem; gap: 0.5rem;">
1186+
<span style="font-size: 0.9rem; font-weight: 500; color: var(--text-primary);">Rules</span>
1187+
<button class="section-toggle collapsed" onclick="toggleSection('sigma-rules')"></button>
1188+
</div>
1189+
<div class="section-content collapsed" id="sigma-rules-content" style="margin-top: 0.5rem;">
1190+
<div id="sigma-rules-container" style="font-size: 0.875rem; color: var(--text-primary);"></div>
1191+
</div>
1192+
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 0.75rem; gap: 0.5rem;">
1193+
<span style="font-size: 0.9rem; font-weight: 500; color: var(--text-primary);">Matches</span>
1194+
<button class="section-toggle" onclick="toggleSection('sigma-matches')"></button>
1195+
</div>
1196+
<div class="section-content" id="sigma-matches-content" style="margin-top: 0.5rem;">
1197+
<div id="sigma-matches-container" style="font-size: 0.875rem; color: var(--text-primary);"></div>
1198+
</div>
1199+
</div>
1200+
</div>
1201+
11801202
<div class="section">
11811203
<h2>📊 Analysis Results</h2>
11821204
<div id="analysis-timestamp" style="color: var(--text-secondary); font-size: 0.875rem; margin-bottom: 1rem; display: none;">
@@ -1572,7 +1594,70 @@ <h3>📋 Full Data (JSON)</h3>
15721594
return 'Error calculating hash';
15731595
}
15741596
}
1575-
1597+
1598+
// Parse Sigma rule YAML for title, id, description, status, level (best-effort, no external deps)
1599+
function parseSigmaRuleMeta(yamlText) {
1600+
const o = { title: null, id: null, description: null, status: null, level: null };
1601+
if (!yamlText || typeof yamlText !== 'string') return o;
1602+
const m = (name, pat) => { const r = yamlText.match(pat); if (r) o[name] = r[1].trim(); };
1603+
m('title', /^title:\s*(.+)$/m);
1604+
m('id', /^id:\s*(.+)$/m);
1605+
m('status', /^status:\s*(.+)$/m);
1606+
m('level', /^level:\s*(.+)$/m);
1607+
const desc = yamlText.match(/^description:\s*(.+)$/m);
1608+
if (desc) o.description = desc[1].replace(/\s+/g, ' ').trim();
1609+
return o;
1610+
}
1611+
1612+
// Fetch Sigma rules from server
1613+
async function fetchSigmaRules() {
1614+
const urls = ['/rules/novispy.yml', '/rules/cellebrite.yml'];
1615+
console.log('[fetchSigmaRules] Fetching Sigma rules:', urls.join(', '));
1616+
try {
1617+
const fetchOne = async (url) => {
1618+
try {
1619+
const res = await fetch(url);
1620+
const text = res.ok ? await res.text() : '';
1621+
const success = !!(res.ok && (text || '').length > 0);
1622+
if (success) {
1623+
console.log(`[fetchSigmaRules] ✓ ${url}: ${res.status}, ${(text || '').length} chars`);
1624+
} else {
1625+
console.warn(`[fetchSigmaRules] ✗ ${url}: ${res.status} ${res.statusText}`);
1626+
}
1627+
return { text, success, httpStatus: res.status, fetchError: success ? null : (res.status + ' ' + (res.statusText || '')), url };
1628+
} catch (e) {
1629+
console.warn(`[fetchSigmaRules] ✗ ${url}:`, e);
1630+
return { text: '', success: false, httpStatus: null, fetchError: 'Network error: ' + (e.message || String(e)), url };
1631+
}
1632+
};
1633+
const results = await Promise.all(urls.map(u => fetchOne(u)));
1634+
const combined = results.filter(r => (r.text || '').length > 0).map(r => r.text).join('\n---\n');
1635+
const rulesInfo = results.map(r => {
1636+
const meta = r.success ? parseSigmaRuleMeta(r.text) : {};
1637+
return {
1638+
url: r.url,
1639+
success: r.success,
1640+
fetchError: r.fetchError || null,
1641+
title: meta.title ?? null,
1642+
id: meta.id ?? null,
1643+
description: meta.description ?? null,
1644+
status: meta.status ?? null,
1645+
level: meta.level ?? null
1646+
};
1647+
});
1648+
const count = rulesInfo.filter(r => r.success).length;
1649+
if (count > 0) {
1650+
console.log(`[fetchSigmaRules] Loaded ${count} rule file(s), ${combined.length} chars total`);
1651+
} else {
1652+
console.warn('[fetchSigmaRules] No rules loaded (all fetches failed or empty)');
1653+
}
1654+
return { rules: combined, rulesInfo };
1655+
} catch (e) {
1656+
console.warn('[fetchSigmaRules] Could not fetch Sigma rules:', e);
1657+
return { rules: '', rulesInfo: [] };
1658+
}
1659+
}
1660+
15761661
// Download results
15771662
window.downloadResults = function() {
15781663
if (!currentResults) {
@@ -1587,6 +1672,8 @@ <h3>📋 Full Data (JSON)</h3>
15871672
battery_info: currentResults.battery_info,
15881673
process_count: currentResults.process_count,
15891674
package_count: currentResults.package_count,
1675+
sigma_matches: currentResults.sigma_matches || null,
1676+
sigma_rules_info: currentResults.sigma_rules_info || [],
15901677
packages: currentResults.packages,
15911678
processes: currentResults.processes,
15921679
battery_apps: currentResults.battery_apps,
@@ -1625,6 +1712,8 @@ <h3>📋 Full Data (JSON)</h3>
16251712
battery_info: currentResults.battery_info,
16261713
process_count: currentResults.process_count,
16271714
package_count: currentResults.package_count,
1715+
sigma_matches: currentResults.sigma_matches || null,
1716+
sigma_rules_info: currentResults.sigma_rules_info || [],
16281717
logcat_lines: currentResults.logcat_lines,
16291718
has_security_analysis: currentResults.has_security_analysis,
16301719
packages: currentResults.packages,
@@ -1993,6 +2082,52 @@ <h3>📋 Full Data (JSON)</h3>
19932082
document.getElementById('stats-processes').textContent = data.process_count || 0;
19942083
document.getElementById('stats-packages').textContent = data.package_count || 0;
19952084

2085+
// Detection / Sigma rules (title, id, description, status, level, fetch success)
2086+
const sigmaEl = document.getElementById('sigma-rules-container');
2087+
const info = data.sigma_rules_info;
2088+
if (info && Array.isArray(info) && info.length > 0) {
2089+
sigmaEl.innerHTML = info.map(r => {
2090+
const fetchBadge = r.success
2091+
? '<span style="color: var(--success);">✓ Fetched</span>'
2092+
: '<span style="color: var(--error);">✗ ' + escapeHtml(r.fetchError || 'Failed') + '</span>';
2093+
const rows = [
2094+
['URL', escapeHtml(r.url)],
2095+
['Server', fetchBadge],
2096+
['Title', r.title ? escapeHtml(r.title) : '-'],
2097+
['ID', r.id ? escapeHtml(r.id) : '-'],
2098+
['Description', r.description ? escapeHtml(r.description) : '-'],
2099+
['Status', r.status ? escapeHtml(r.status) : '-'],
2100+
['Level', r.level ? escapeHtml(r.level) : '-']
2101+
];
2102+
const body = rows.map(([l, v]) => `<div style="margin-bottom: 0.35rem;"><span style="color: var(--text-secondary); font-weight: 500;">${escapeHtml(l)}:</span> ${v}</div>`).join('');
2103+
return `<div class="result-card" style="margin-bottom: 0.75rem; padding: 1rem;"><div style="font-size: 0.875rem;">${body}</div></div>`;
2104+
}).join('');
2105+
} else {
2106+
sigmaEl.textContent = 'No rules loaded.';
2107+
}
2108+
2109+
// Sigma matches (from match_with_log_to_value: rule_id, rule_title, level, log_entry)
2110+
const matchesEl = document.getElementById('sigma-matches-container');
2111+
const matches = data.sigma_matches;
2112+
if (matches && Array.isArray(matches) && matches.length > 0) {
2113+
matchesEl.innerHTML = matches.map(m => {
2114+
const id = (m.rule_id && m.rule_id.trim()) ? escapeHtml(m.rule_id) : '-';
2115+
const title = (m.rule_title && m.rule_title.trim()) ? escapeHtml(m.rule_title) : '-';
2116+
const level = (m.level && m.level.trim()) ? escapeHtml(m.level) : '-';
2117+
const l = (m.level || '').toLowerCase();
2118+
const levelColor = l === 'critical' ? 'var(--error)' : l === 'high' ? 'var(--warning)' : 'var(--text-secondary)';
2119+
let matchedLogHtml = '';
2120+
const ml = m.log_entry ?? m.matched_log;
2121+
if (ml && typeof ml === 'object' && !Array.isArray(ml) && Object.keys(ml).length > 0) {
2122+
const fmt = (v) => typeof v === 'object' && v !== null ? escapeHtml(JSON.stringify(v)) : escapeHtml(String(v ?? ''));
2123+
matchedLogHtml = '<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border);"><span style="color: var(--text-secondary); font-weight: 500;">Matched log:</span><div style="margin-top: 0.35rem; font-family: monospace; font-size: 0.8rem;">' + Object.entries(ml).map(([k, v]) => '<div style="margin-bottom: 0.2rem;"><span style="color: var(--text-secondary);">' + escapeHtml(k) + ':</span> ' + fmt(v) + '</div>').join('') + '</div></div>';
2124+
}
2125+
return `<div class="result-card" style="margin-bottom: 0.5rem; padding: 0.75rem 1rem;"><div style="font-size: 0.875rem;"><div style="margin-bottom: 0.25rem;"><span style="color: var(--text-secondary); font-weight: 500;">Rule:</span> ${title}</div><div style="display: flex; gap: 1rem; flex-wrap: wrap;"><span><span style="color: var(--text-secondary);">ID:</span> ${id}</span><span style="color: ${levelColor}; font-weight: 500;">${level}</span></div>${matchedLogHtml}</div></div>`;
2126+
}).join('');
2127+
} else {
2128+
matchesEl.textContent = 'No matches.';
2129+
}
2130+
19962131
// File info
19972132
const fileInfoCard = document.getElementById('file-info-card');
19982133
if (data.file_info) {
@@ -3433,8 +3568,10 @@ <h3 style="margin-bottom: 1rem;">Socket Details</h3>
34333568

34343569
showStatus('Hash calculated! Analyzing...', 'info');
34353570

3436-
const result = await adb.analyze_bugreport(bugreportData);
3437-
3571+
const { rules, rulesInfo } = await fetchSigmaRules();
3572+
const result = await adb.analyze_bugreport(bugreportData, rules);
3573+
result.sigma_rules_info = rulesInfo;
3574+
34383575
result.file_info = {
34393576
path: 'Generated on device',
34403577
size_bytes: bugreportData.length,
@@ -3502,10 +3639,12 @@ <h3 style="margin-bottom: 1rem;">Socket Details</h3>
35023639

35033640
showStatus(`✓ Hash calculated in ${formatDuration(hashDurationMs)}! Now analyzing...`, 'info');
35043641
showLoadingSpinner('Analyzing bugreport... This may take a minute');
3505-
3642+
3643+
const { rules, rulesInfo } = await fetchSigmaRules();
35063644
const startAnalysis = Date.now();
3507-
const result = await adb.analyze_bugreport(bugreportData);
3508-
3645+
const result = await adb.analyze_bugreport(bugreportData, rules);
3646+
result.sigma_rules_info = rulesInfo;
3647+
35093648
const analysisDurationMs = Date.now() - startAnalysis;
35103649
const totalDurationMs = Date.now() - startDownload;
35113650

@@ -3635,8 +3774,10 @@ <h3 style="margin-bottom: 1rem;">Socket Details</h3>
36353774
window.adb = adb;
36363775
}
36373776

3638-
const result = await adb.analyze_bugreport(uint8Array);
3639-
3777+
const { rules, rulesInfo } = await fetchSigmaRules();
3778+
const result = await adb.analyze_bugreport(uint8Array, rules);
3779+
result.sigma_rules_info = rulesInfo;
3780+
36403781
result.file_info = {
36413782
path: uploadedFile.name,
36423783
size_bytes: uploadedFile.size,

favicon.ico

15 KB
Binary file not shown.

0 commit comments

Comments
 (0)