-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsafe-browsing-block.js
More file actions
153 lines (137 loc) · 6.37 KB
/
Copy pathsafe-browsing-block.js
File metadata and controls
153 lines (137 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* WardenOne — Copyright (C) 2026 iri
Licensed under the GNU General Public License v3 or later. See LICENSE.
Official source: https://github.com/iri-dev/WardenOne
Upstream filter-list attribution: CREDITS.md
Redistributing a modified copy? GPLv3 section 5(a) requires you to mark it as changed,
with the date, and to keep these notices intact. */
(function () {
'use strict';
// This page's URL carries one thing: a handle. The blocked address lives in a
// storage.session record the worker wrote just before sending the tab here, so it never
// enters the tab's URL -- and from there browser history, session restore or a screenshot.
// The record is read directly, without the worker, so it works after a worker eviction; it
// is consumed when the reader continues anyway, and a page whose record is gone offers only
// the way back (PRIV-04).
const handle = new URLSearchParams(location.search || '').get('w') || '';
const RECORD_KEY = /^[0-9a-f]{32}$/.test(handle) ? 'wardenone_warning:' + handle : '';
function loadRecord() {
return new Promise((resolve) => {
if (!RECORD_KEY) { resolve(null); return; }
try {
chrome.storage.session.get(RECORD_KEY, (res) => {
void chrome.runtime.lastError;
const rec = res && res[RECORD_KEY];
resolve(rec && rec.kind === 'safe-browsing' ? rec : null);
});
} catch (_) { resolve(null); }
});
}
// One remove, then go. The record is its own storage key, so consuming it cannot race
// with anything else being written; the timer only guards against a callback that
// never fires.
function consumeRecord(then) {
let done = false;
const go = () => { if (!done) { done = true; then(); } };
try { chrome.storage.session.remove(RECORD_KEY, go); } catch (_) { go(); }
setTimeout(go, 800);
}
// Validate the scheme here rather than relying on a check in another file. The worker only
// stores http(s), but the guarantee that this page never navigates anywhere else rests here,
// the same way it does in its siblings.
function safeHttpUrl(raw) {
try {
const u = new URL(String(raw || ''));
return (u.protocol === 'http:' || u.protocol === 'https:') ? u.href : '';
} catch (_) {
return '';
}
}
loadRecord().then((rec) => {
const url = safeHttpUrl(rec && rec.url);
const provider = (rec && rec.provider) || 'URL reputation';
const threats = String((rec && rec.threats) || '')
.split(',')
.map((t) => t.trim())
.filter(Boolean)
.map((t) => t.replace(/_/g, ' ').toLowerCase());
// What is painted is the display form the worker built: host and path, no query.
const site = document.getElementById('site');
if (site) site.textContent = rec ? (rec.shown || '') : 'This warning has expired';
const providerEl = document.getElementById('provider');
if (providerEl) providerEl.textContent = 'Blocked by WardenOne + ' + provider;
const reason = document.getElementById('reason');
if (reason) {
reason.textContent = !rec
? 'The address this warning was about is no longer held. Go back to where you were.'
: (threats.length
? provider + ' flagged this URL as ' + threats.join(', ') + '.'
: provider + ' flagged this URL as dangerous.');
}
document.getElementById('back')?.addEventListener('click', () => {
if (history.length > 1) history.back();
else location.href = 'about:blank';
});
document.getElementById('activity')?.addEventListener('click', () => {
location.href = chrome.runtime.getURL('history.html');
});
let host = '';
try { host = new URL(url).hostname; } catch (_) {}
const escapePanel = document.getElementById('escape');
const proceed = document.getElementById('proceed');
const foot = document.getElementById('foot');
const escapeHost = document.getElementById('escape-host');
if (escapeHost) escapeHost.textContent = host || 'this site';
// The escape route is revealed rather than shown, and the button that uses it stays
// disabled for a few seconds. Someone who genuinely knows this site is a false
// positive will wait; someone clicking through a malware warning on reflex will not.
// No safe destination means there is nothing to continue TO, so the escape hatch is not offered
// at all rather than being offered and then failing at the last step.
if (!url) {
const wrongBtn = document.getElementById('wrong');
if (wrongBtn) wrongBtn.hidden = true;
}
document.getElementById('wrong')?.addEventListener('click', () => {
if (!escapePanel || !url) return;
escapePanel.hidden = false;
if (foot) foot.hidden = true;
document.getElementById('wrong')?.setAttribute('disabled', 'disabled');
let left = 5;
if (proceed) {
proceed.textContent = 'Continue anyway (' + left + ')';
const tick = setInterval(() => {
left--;
if (left > 0) { proceed.textContent = 'Continue anyway (' + left + ')'; return; }
clearInterval(tick);
proceed.textContent = 'Continue anyway';
proceed.removeAttribute('disabled');
}, 1000);
}
});
document.getElementById('report')?.addEventListener('click', () => {
const title = 'False positive: ' + (host || 'site blocked');
const body = [
'A site was blocked that I believe is safe.',
'',
'Site: ' + (host || '(unknown)'),
'Reported by: ' + provider,
'Flagged as: ' + (threats.length ? threats.join(', ') : '(not stated)'),
'',
'Why I think this is wrong:',
'',
].join('\n');
const target = 'https://github.com/iri-dev/WardenOne/issues/new'
+ '?title=' + encodeURIComponent(title)
+ '&body=' + encodeURIComponent(body);
try { chrome.tabs.create({ url: target }); } catch (_) { window.open(target, '_blank'); }
});
proceed?.addEventListener('click', () => {
if (proceed.hasAttribute('disabled') || !url) return;
proceed.setAttribute('disabled', 'disabled');
proceed.textContent = 'Continuing…';
chrome.runtime.sendMessage({ kind: 'safe-browsing-allow-once', host: host }, (res) => { void chrome.runtime.lastError;
if (res && res.ok) { consumeRecord(() => { location.href = url; }); return; }
proceed.textContent = 'Could not continue';
});
});
});
})();