Skip to content

Commit cbaf8b7

Browse files
committed
Add safe browsing support
1 parent 8d0cb7f commit cbaf8b7

File tree

6 files changed

+83
-11
lines changed

6 files changed

+83
-11
lines changed

app.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ app.get('/', (req, res) => {
3434
res.sendFile(__dirname + '/public/index.html');
3535
});
3636

37+
let lookup;
38+
async function checkURL(url){
39+
if(!lookup) lookup = require('safe-browse-url-lookup')({ apiKey: config.SAFE_BROWSING_APIKEY });
40+
return await lookup.checkSingle(url)
41+
}
42+
3743
function generateCode() {
3844
const buffer = crypto.randomBytes(1);
3945
const hex = buffer.toString('hex');
@@ -120,18 +126,24 @@ app.get("/s/:code", async (req, res) => {
120126
if (password) {
121127
let hashPass = SHA256(password).toString();
122128
if (hashPass === db[code].password) {
123-
res.redirect(db[code].link);
129+
let isSafe = !(await checkURL(db[code].link));
130+
if (isSafe) res.redirect(db[code].link);
131+
else res.redirect(`/warning?link=${Base64.encode(db[code].link)}`);
124132
} else {
125133
res.redirect(`/s/${code}`)
126134
}
127135
} else {
128136
res.sendFile(__dirname + '/public/password.html');
129137
}
130138
} else {
131-
res.redirect(db[code].link);
139+
let isSafe = !(await checkURL(db[code].link));
140+
if (isSafe) res.redirect(db[code].link);
141+
else res.redirect(`/warning?link=${Base64.encode(db[code].link)}`);
132142
}
133143
} else {
134-
res.redirect(db[code]);
144+
let isSafe = !(await checkURL(db[code]));
145+
if (isSafe) res.redirect(db[code]);
146+
else res.redirect(`/warning?link=${Base64.encode(db[code])}`);
135147
}
136148
} else if (isMongoDB) {
137149
await client.connect();
@@ -152,15 +164,19 @@ app.get("/s/:code", async (req, res) => {
152164
if (password) {
153165
let hashPass = SHA256(password).toString();
154166
if (hashPass === filtered[0].password) {
155-
res.redirect(filtered[0].link);
167+
let isSafe = !(await checkURL(filtered[0].link));
168+
if (isSafe) res.redirect(filtered[0].link);
169+
else res.redirect(`/warning?link=${Base64.encode(filtered[0].link)}`);
156170
} else {
157171
res.redirect(`/s/${code}`)
158172
}
159173
} else {
160174
res.sendFile(__dirname + '/public/password.html');
161175
}
162176
} else {
163-
res.redirect(filtered[0].link);
177+
let isSafe = !(await checkURL(filtered[0].link));
178+
if (isSafe) res.redirect(filtered[0].link);
179+
else res.redirect(`/warning?link=${Base64.encode(filtered[0].link)}`);
164180
}
165181
}
166182
});
@@ -188,18 +204,21 @@ app.get("/api/s/:code", async (req, res) => {
188204
if (password) {
189205
let hashPass = SHA256(password).toString();
190206
if (hashPass === db[code].password) {
191-
res.json({status: 200, data: {original: db[code].link, shorten: `${config.DOMAIN}/s/${code}`}});
207+
let isSafe = !(await checkURL(db[code].link));
208+
res.json({status: 200, data: {original: db[code].link, shorten: `${config.DOMAIN}/s/${code}`, safe: isSafe }});
192209
} else {
193210
res.json({status: 401, data: { original: "Error: Unauthorized", shorten: `${config.DOMAIN}/s/${code}` }})
194211
}
195212
} else {
196213
res.json({status: 401, data: { original: "Error: Unauthorized", shorten: `${config.DOMAIN}/s/${code}` }})
197214
}
198215
} else {
199-
res.json({status: 200, data: {original: db[code].link, shorten: `${config.DOMAIN}/s/${code}`}});
216+
let isSafe = !(await checkURL(db[code].link));
217+
res.json({status: 200, data: {original: db[code].link, shorten: `${config.DOMAIN}/s/${code}`, safe: isSafe }});
200218
}
201219
} else {
202-
res.json({status: 200, data: {original: db[code], shorten: `${config.DOMAIN}/s/${code}`}});
220+
let isSafe = !(await checkURL(db[code]));
221+
res.json({status: 200, data: {original: db[code], shorten: `${config.DOMAIN}/s/${code}`, safe: isSafe }});
203222
}
204223
} else if (isMongoDB) {
205224
await client.connect();
@@ -241,6 +260,10 @@ app.get("/generated", (req, res) => {
241260
res.sendFile(__dirname + '/public/generated.html');
242261
})
243262

263+
app.get("/warning", (req, res) => {
264+
res.sendFile(__dirname + '/public/warning.html');
265+
});
266+
244267
app.listen(process.env.PORT || config.PORT, async () => {
245268
if (isJSON) {
246269
if (!config.DB_JSON_PATH) {

config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module.exports = {
44

55
// The domain name of the website
66
DOMAIN: "http://localhost",
7+
SAFE_BROWSING_APIKEY: "AIzaSyBovR1bR-aI4KiXZ9AB8MFLIqYROK2V57Y",
78

89
// Database type: json, mongodb
910
DB_TYPE: "json",

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quecto",
3-
"version": "1.0.5",
3+
"version": "1.0.5g",
44
"description": "",
55
"main": "index.js",
66
"scripts": {
@@ -18,6 +18,7 @@
1818
"js-base64": "^3.7.5",
1919
"mongodb": "^5.7.0",
2020
"multer": "^1.4.5-lts.1",
21-
"nodemon": "^3.0.1"
21+
"nodemon": "^3.0.1",
22+
"safe-browse-url-lookup": "^0.1.1"
2223
}
2324
}

public/generated.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
const params = new URLSearchParams(window.location.search);
3030
const link = params.get("link");
3131

32-
3332
form.link.value = Base64.decode(link);
3433
document.getElementById("qrcode").src = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${Base64.decode(link)}`;
3534
</script>

public/style.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,26 @@ form input[type="submit"] {
5151
position: absolute;
5252
bottom: 10px;
5353
right: 10px;
54+
}
55+
56+
h1 {
57+
color: red;
58+
font-size: 30px;
59+
font-family: Arial, Helvetica, sans-serif;
60+
}
61+
62+
button {
63+
background: #2980b9;
64+
65+
border: none;
66+
outline: none;
67+
border-radius: 10px;
68+
69+
font-size: 30px;
70+
padding: 15px;
71+
cursor: pointer;
72+
73+
width: 52vw;
74+
color: white;
75+
height: auto !important;
5476
}

public/warning.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Quecto</title>
6+
7+
<link rel="stylesheet" href="/public/style.css">
8+
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.7.5/base64.min.js"></script>
9+
</head>
10+
<body>
11+
<form>
12+
<div class="content">
13+
<h1>Warning ! Google has identified the link as potentially dangerous.</h1>
14+
<button>Continue</button>
15+
</div>
16+
</form>
17+
18+
<script>
19+
const params = new URLSearchParams(window.location.search);
20+
const link = params.get("link");
21+
document.querySelector("button").onclick = () => window.location.href = Base64.decode(link);
22+
23+
document.querySelector("form").onsubmit = (e) => e.preventDefault();
24+
</script>
25+
</body>
26+
</html>

0 commit comments

Comments
 (0)