Skip to content

Commit 49ffd31

Browse files
committed
Fixes #383
1 parent 66d0008 commit 49ffd31

File tree

5 files changed

+384
-306
lines changed

5 files changed

+384
-306
lines changed

chrome/js/background.js

Lines changed: 117 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,148 @@
11
/* global chrome, console, exports, CryptoJS, Emitter */
22

3-
var repoUrl = "https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json";
4-
var updatedAt = Date.now();
5-
var repo;
6-
var repoFuncs;
7-
8-
var vulnerable = {};
9-
var events = new Emitter();
10-
var sandboxWin;
11-
var scanEnabled = true;
12-
13-
var hasher = {
14-
sha1 : function(data) {
15-
return CryptoJS.SHA1(data).toString(CryptoJS.enc.Hex);
16-
}
3+
const repoUrl =
4+
"https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json";
5+
let updatedAt = Date.now();
6+
let repo;
7+
let repoFuncs;
8+
9+
let vulnerable = {};
10+
const events = new Emitter();
11+
let sandboxWin;
12+
13+
const hasher = {
14+
sha1: function (data) {
15+
return CryptoJS.SHA1(data).toString(CryptoJS.enc.Hex);
16+
},
1717
};
1818

19-
function download(url) {
20-
var events = new Emitter();
21-
var xhr = new XMLHttpRequest();
22-
xhr.onreadystatechange = function() {
23-
if (xhr.readyState == 4) {
24-
if (xhr.status == 200) {
25-
events.emit('success', xhr.responseText);
26-
} else {
27-
console.log("Got " + xhr.status + " when trying to download " + url);
28-
}
29-
}
30-
return true;
31-
};
32-
xhr.open("GET", url, true);
33-
xhr.send();
34-
return events;
19+
async function download(url) {
20+
const response = await fetch(url);
21+
if (response.ok) {
22+
return response.text();
23+
} else {
24+
throw new Error(
25+
"Got " + response.status + " when trying to download " + url
26+
);
27+
}
3528
}
3629

37-
function downloadRepo() {
38-
var events = new Emitter();
39-
console.log("Downloading repo ...");
40-
updatedAt = Date.now();
41-
download(repoUrl + "?" + updatedAt).on('success', function(repoData) {
42-
repo = JSON.parse(retire.replaceVersion(repoData));
43-
console.log("Done");
44-
vulnerable = {};
45-
setFuncs();
46-
events.emit('success');
47-
return true;
48-
});
49-
return events;
30+
async function downloadRepo() {
31+
console.log("Downloading repo ...");
32+
updatedAt = Date.now();
33+
const repoData = await download(repoUrl + "?" + updatedAt);
34+
repo = JSON.parse(retire.replaceVersion(repoData));
35+
console.log("Done");
36+
vulnerable = {};
37+
setFuncs();
5038
}
5139

5240
function setFuncs() {
53-
repoFuncs = {};
54-
for (var component in repo) {
55-
if (repo[component].extractors.func) {
56-
repoFuncs[component] = repo[component].extractors.func;
57-
}
58-
}
41+
repoFuncs = {};
42+
for (var component in repo) {
43+
if (repo[component].extractors.func) {
44+
repoFuncs[component] = repo[component].extractors.func;
45+
}
46+
}
5947
}
6048

6149
function getFileName(url) {
62-
var a = document.createElement("a");
63-
a.href = url;
64-
return (a.pathname.match(/\/([^\/?#]+)$/i) || [,""])[1];
50+
var a = document.createElement("a");
51+
a.href = url;
52+
return (a.pathname.match(/\/([^\/?#]+)$/i) || [, ""])[1];
6553
}
6654

67-
68-
69-
events.on('scan', function(details) {
70-
if (details.url.indexOf('chrome-extension://') === 0) return true;
71-
72-
if ((Date.now() - updatedAt) > 1000*60*60*6) {
73-
downloadRepo().on('success', function() { events.emit('scan', details); });
74-
return true;
75-
}
76-
events.emit('result-ready', details, []);
77-
console.log("Scanning " + details.url + " ...");
78-
var results = retire.scanUri(details.url, repo);
79-
if (results.length > 0) {
80-
events.emit('result-ready', details, results);
81-
return true;
82-
}
83-
results = retire.scanFileName(getFileName(details.url), repo);
84-
if (results.length > 0) {
85-
events.emit('result-ready', details, results);
86-
return true;
87-
}
88-
download(details.url).on('success', function(content) {
89-
events.emit('script-downloaded', details, content);
90-
return true;
91-
});
92-
return true;
55+
events.on("scan", function (details) {
56+
if (details.url.indexOf("chrome-extension://") === 0) return true;
57+
58+
if (Date.now() - updatedAt > 1000 * 60 * 60 * 6) {
59+
downloadRepo().then(() => {
60+
events.emit("scan", details);
61+
});
62+
return;
63+
}
64+
events.emit("result-ready", details, []);
65+
console.log("Scanning " + details.url + " ...");
66+
var results = retire.scanUri(details.url, repo);
67+
if (results.length > 0) {
68+
events.emit("result-ready", details, results);
69+
return;
70+
}
71+
results = retire.scanFileName(getFileName(details.url), repo);
72+
if (results.length > 0) {
73+
events.emit("result-ready", details, results);
74+
return;
75+
}
76+
download(details.url).then((content) => {
77+
events.emit("script-downloaded", details, content);
78+
});
9379
});
9480

95-
events.on('script-downloaded', function(details, content) {
96-
var results = retire.scanFileContent(content, repo, hasher);
97-
if (results.length > 0) {
98-
events.emit('result-ready', details, results);
99-
return true;
100-
}
101-
events.emit('sandbox', details, content);
102-
console.log(hasher.sha1(content) + " : " + details.url);
103-
return true;
81+
events.on("script-downloaded", function (details, content) {
82+
var results = retire.scanFileContent(content, repo, hasher);
83+
if (results.length > 0) {
84+
events.emit("result-ready", details, results);
85+
return true;
86+
}
87+
events.emit("sandbox", details, content);
88+
console.log(hasher.sha1(content) + " : " + details.url);
89+
return true;
10490
});
10591

106-
events.on('sandbox', function(details, content) {
107-
sandboxWin.postMessage({ tabId: details.tabId, script : content, url: details.url, repoFuncs: repoFuncs }, "*");
108-
return true;
92+
events.on("sandbox", function (details, content) {
93+
sandboxWin.postMessage(
94+
{
95+
tabId: details.tabId,
96+
script: content,
97+
url: details.url,
98+
repoFuncs: repoFuncs,
99+
},
100+
"*"
101+
);
102+
return true;
109103
});
110104

111-
window.addEventListener("message", function(evt) {
112-
if (evt.data.version) {
113-
var results = retire.check(evt.data.component, evt.data.version, repo);
114-
console.log("SANDBOX", stringifyResults(results));
115-
events.emit('result-ready', { url : evt.data.original.url, tabId : evt.data.original.tabId }, results);
116-
}
117-
return true;
105+
window.addEventListener("message", function (evt) {
106+
if (evt.data.version) {
107+
var results = retire.check(evt.data.component, evt.data.version, repo);
108+
console.log("SANDBOX", stringifyResults(results));
109+
events.emit(
110+
"result-ready",
111+
{ url: evt.data.original.url, tabId: evt.data.original.tabId },
112+
results
113+
);
114+
}
115+
return true;
118116
});
119117

120118
function stringifyResults(results) {
121-
return results.map(x => "\n" + x.component + ":" + x.version).reduce((a,b) => a + b, "");
119+
return results
120+
.map((x) => "\n" + x.component + ":" + x.version)
121+
.reduce((a, b) => a + b, "");
122122
}
123123

124-
events.on('result-ready', function(details, results) {
125-
var vulnerable = retire.isVulnerable(results);
126-
if (vulnerable) {
127-
console.log(details.url, stringifyResults(results));
128-
chrome.browserAction.setBadgeText({text : "!", tabId : details.tabId });
129-
}
130-
if (!vulnerable) console.log(details.url, stringifyResults(results));
131-
132-
vulnerable[details.url] = results;
133-
134-
var result = { vulnerable: vulnerable, results: results, url: details.url };
135-
setTimeout(function() {
136-
if (details.tabId >= 0) {
137-
chrome.tabs.sendMessage(details.tabId, {
138-
message : JSON.stringify(result)
139-
}, function(response) {
140-
let e = chrome.runtime.lastError
141-
if (e) console.log("Failed to send message:" + e);
142-
console.log(details.tabId);
143-
if (response) {
144-
chrome.browserAction.setBadgeText({text : "" + response.count, tabId : details.tabId });
145-
}
146-
return true;
147-
});
148-
}
149-
return true;
150-
}, 3000);
151-
});
124+
events.on("result-ready", function (details, results) {
125+
var vulnerable = retire.isVulnerable(results);
126+
if (vulnerable) {
127+
console.log(details.url, stringifyResults(results));
128+
}
129+
if (!vulnerable) console.log(details.url, stringifyResults(results));
152130

153-
chrome.browserAction.setBadgeBackgroundColor({ color: [255, 0, 0, 255] });
154-
155-
156-
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
157-
if (request.to !== 'background') {
158-
return true;
159-
}
160-
if (request.message === 'enabled?') {
161-
sendResponse({ enabled : scanEnabled });
162-
return true;
163-
}
164-
if (request.message === 'enable') {
165-
scanEnabled = request.data;
166-
}
167-
return true;
168-
});
131+
vulnerable[details.url] = results;
169132

133+
var result = { vulnerable: vulnerable, results: results, url: details.url };
134+
chrome.runtime.sendMessage({ type: "result", result, details });
135+
});
170136

171-
downloadRepo().on('success', function() {
172-
var filter = {
173-
"urls" : ["<all_urls>"],
174-
"types" : ["script"]
175-
};
176-
function scan(details) {
177-
if (details.method === "GET" && scanEnabled) {
178-
events.emit('scan', details);
179-
}
180-
return true;
181-
}
182-
chrome.webRequest.onCompleted.addListener(scan, filter, []);
183-
return true;
137+
downloadRepo().then(() => {
138+
chrome.runtime.sendMessage({ type: "repo-ready" });
139+
chrome.runtime.onMessage.addListener((msg) => {
140+
if (msg.type === "scan") {
141+
events.emit("scan", msg.details);
142+
} else {
143+
console.log("Background", msg);
144+
}
145+
});
184146
});
185147

186148
sandboxWin = window.document.getElementById("sandboxframe").contentWindow;
187-
188-
189-
190-
191-

chrome/js/content.js

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
/* global chrome, console */
22

3-
(function() {
4-
var count = 0;
5-
var totalResults = [];
6-
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
7-
if (request.message) {
8-
var result = JSON.parse(request.message);
9-
totalResults.push(result);
10-
if (result.vulnerable) {
11-
count++;
12-
sendResponse({'count' : count});
13-
var out = [];
14-
result.results.forEach(function(r) {
15-
r.vulnerabilities = r.vulnerabilities || [];
16-
out.push(r.component + " " + r.version + " - Info: " +
17-
r.vulnerabilities.map(function(i) { return i.info }).flatten().join(" "));
18-
})
19-
console.log("⚠️ Loaded script with known vulnerabilities: " + result.url + "\n - " + out.join("\n - "));
20-
}
21-
} else if (request.getDetected) {
22-
sendResponse(totalResults);
23-
}
24-
return true;
25-
});
26-
})();
3+
let count = 0;
4+
const totalResults = [];
275

28-
Array.prototype.flatten = function() { return this.reduce((a,b) => a.concat(b), []) }
6+
(function () {
7+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
8+
if (request.message) {
9+
const result = JSON.parse(request.message);
10+
totalResults.push(result);
11+
if (result.vulnerable) {
12+
count++;
13+
sendResponse({ count: count });
14+
const out = result.results.map((r) => {
15+
r.vulnerabilities = r.vulnerabilities || [];
16+
return `${r.component} ${r.version} - Info: ${r.vulnerabilities
17+
.map((i) => i.info)
18+
.join(" ")}`;
19+
});
20+
console.log(
21+
`⚠️ Loaded script with known vulnerabilities: ${
22+
result.url
23+
}\n - ${out.join("\n - ")}`
24+
);
25+
}
26+
} else if (request.getDetected) {
27+
sendResponse(totalResults);
28+
}
29+
});
30+
})();

0 commit comments

Comments
 (0)