summaryrefslogtreecommitdiff
path: root/data/extensions/jsr@javascriptrestrictor/fp_report.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/fp_report.js')
-rw-r--r--data/extensions/jsr@javascriptrestrictor/fp_report.js436
1 files changed, 253 insertions, 183 deletions
diff --git a/data/extensions/jsr@javascriptrestrictor/fp_report.js b/data/extensions/jsr@javascriptrestrictor/fp_report.js
index 2606dc9..f9f44e3 100644
--- a/data/extensions/jsr@javascriptrestrictor/fp_report.js
+++ b/data/extensions/jsr@javascriptrestrictor/fp_report.js
@@ -21,18 +21,20 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
+let hiddenTraces = {};
+
/**
* Event listener that listens for a load of FPD report page. If the page is loaded, fetch FPD data from background.
*
* \param callback Function that initializes FPD report creation from fetched data.
*/
window.addEventListener('load', () => {
- browser.runtime.sendMessage({
- purpose: "fpd-get-report-data",
- tabId: new URLSearchParams(window.location.search).get("id")
- }).then((result) => {
- createReport(result);
- });
+ browser.runtime.sendMessage({
+ purpose: "fpd-get-report-data",
+ tabId: new URLSearchParams(window.location.search).get("id")
+ }).then((result) => {
+ createReport(result);
+ });
});
/**
@@ -41,182 +43,250 @@ window.addEventListener('load', () => {
* \param data Information about latest fingerprinting evaluation consisting of all essential FPD objects.
*/
function createReport(data) {
- var {tabObj, groups, latestEvals, fpDb, exceptionWrappers} = data;
+ var {tabObj, groups, latestEvals, fpDb, exceptionWrappers} = data;
+ var reportContainer = document.getElementById("fpd-report-container");
+ if (!tabObj || !groups || !groups.root || !groups.all || !fpDb || !latestEvals) {
+ report.innerHTML =`<div class="alert">${browser.i18n.getMessage("FPDReportMissingData")}</div>`;
+ return;
+ }
+
+ // parse latestEvals to create more useful representation for FPD report generation
+ var processedEvals = {};
+ for (let item of latestEvals.evalStats) {
+ processedEvals[item.title] = processedEvals[item.title] || {};
+ processedEvals[item.title].type = item.type;
+ let total = 0;
+ if (fpDb[item.title]) {
+ for (let stat of Object.values(fpDb[item.title])) {
+ total += stat.total;
+ }
+ }
+ processedEvals[item.title].total = total;
+ }
+
+ // add page URL and FavIcon to header section of the report
+ if (tabObj) {
+ let urlObj = new URL(tabObj.url);
+ var url = urlObj.hostname + (urlObj.pathname.length > 1 ? urlObj.pathname : "");
+ document.getElementById("report-url").innerHTML = url;
+ let img = document.getElementById("pageFavicon");
+ img.src = tabObj.favIconUrl;
+ }
+
+ var html = "";
+
+ // generate html code for evaluated group
+ let generateGroup = (group) => {
+ if (processedEvals[group]) {
+ if (groups.all[group].description) {
+ html += "<div id=\"" + group + "\" class=\"fpd-group access\">";
+ html += "<h2>" + group + "</h2>";
+ html += `<button class="help" id="expand${group}" >⤵</button>`;
+ html += "<section>";
+ html += "<p>" + groups.all[group].description + "</p>";
+ }
+ for (let [item, type] of Object.entries(groups.all[group].items)) {
+ if (type == "group") {
+ generateGroup(item);
+ }
+ else {
+ generateResource(item)
+ }
+ }
+ if (groups.all[group].description) {
+ html += "</section></div>";
+ }
+ }
+ }
+
+ // generate html code for evaluated resource (get,set,call)
+ let generateResource = (resource) => {
+ if (processedEvals[resource]) {
+ let callers = "";
+ if (fpDb[resource]) {
+ for (let t of Object.values(fpDb[resource])) {
+ let traces = Object.keys(t.callers);
+ for (trace of traces) {
+ if (trace !== "" && !(trace in hiddenTraces)) {
+ callers += "<p>" + trace.replace(/\n/g, '<br>') + "</p>";
+ }
+ }
+ }
+ }
+ let accessRaw = processedEvals[resource].total;
+ let accessCount = accessRaw >= 1000 ? "1000+" : accessRaw;
+ html += `<div class="details ${accessRaw > 0 ? "access" : "no-access"}"><h4><span class="dot">-</span> ` +
+ `${resource} (${exceptionWrappers.includes(resource) ? "n/a" : accessCount})</h4>\n${callers}</div>`;
+ }
+ }
+
+ // start generating FPD report from the first group (root group)
+ generateGroup(groups.root);
var report = document.getElementById("fpd-report");
- if (!tabObj || !groups || !groups.root || !groups.all || !fpDb || !latestEvals) {
- report.innerHTML = browser.i18n.getMessage("FPDReportMissingData");
- return;
- }
-
- // parse latestEvals to create more useful representation for FPD report generation
- var processedEvals = {};
- for (let item of latestEvals.evalStats) {
- processedEvals[item.title] = processedEvals[item.title] || {};
- processedEvals[item.title].type = item.type;
- let total = 0;
- if (fpDb[item.title]) {
- for (let stat of Object.values(fpDb[item.title])) {
- total += stat.total;
- }
- }
- processedEvals[item.title].total = total;
- }
-
- // add page URL and FavIcon to header section of the report
- if (tabObj) {
- let urlObj = new URL(tabObj.url);
- var url = urlObj.hostname + (urlObj.pathname.length > 1 ? urlObj.pathname : "");
- document.getElementById("report-url").innerHTML = url;
- let img = document.getElementById("pageFavicon");
- img.src = tabObj.favIconUrl;
- }
-
- var html = "";
-
- // generate html code for evaluated group
- let generateGroup = (group) => {
- if (processedEvals[group]) {
- if (groups.all[group].description) {
- html += "<div id=\"" + group + "\" class=\"fpd-group access\">";
- html += "<h2>" + group + "</h2>";
- html += "<p>" + groups.all[group].description + "</p>";
- }
- for (let [item, type] of Object.entries(groups.all[group].items)) {
- if (type == "group") {
- generateGroup(item);
- }
- else {
- generateResource(item)
- }
- }
- if (groups.all[group].description) {
- html += "</div>";
- }
- }
- }
-
- // generate html code for evaluated resource (get,set,call)
- let generateResource = (resource) => {
- if (processedEvals[resource]) {
- let accessRaw = processedEvals[resource].total;
- let accessCount = accessRaw >= 1000 ? "1000+" : accessRaw;
- html += `<h4 class="hidden ${accessRaw > 0 ? "access" : "no-access"}"><span class="dot">-</span> ` +
- `${resource} (${exceptionWrappers.includes(resource) ? "n/a" : accessCount})</h4>`;
- }
- }
-
- // start generating FPD report from the first group (root group)
- generateGroup(groups.root);
- report.innerHTML += html;
-
- // process generated document
- let groupElements = document.querySelectorAll(".fpd-group.access");
- for (let i = groupElements.length; i > 0; i--) {
- // remove duplicit entries from groups
- let duplicitArray = [];
- let elements = groupElements[i-1].querySelectorAll(":scope > h4");
- elements.forEach((d) => {
- if (duplicitArray.indexOf(d.innerHTML) > -1) {
- d.remove();
- }
- else {
- duplicitArray.push(d.innerHTML);
- }
- });
-
- // hide groups with no relevant entries
- if (!document.querySelectorAll(`#${groupElements[i-1].id} > .access`).length) {
- groupElements[i-1].classList.replace("access", "no-access");
- }
- }
-
- // function that enables to show accessed resources of the group
- let toggleResources = (event) => {
- let parent = event.target.parentElement;
- for (let i = 0; i < parent.children.length; i++) {
- let child = parent.children[i];
- if (child.tagName == "H4") {
- child.classList.toggle("hidden");
- }
- }
- }
-
- // make group name clickable only if it makes sense - groups with resources
- let makeClickableTitles = () => {
- for (let element of document.querySelectorAll(".fpd-group")) {
- let button;
- let haveChild = false;
-
- for (let i = 0; i < element.children.length; i++) {
- let child = element.children[i];
- if (child.tagName == "H2") {
- button = child;
- }
- if (child.tagName == "H4" && !child.classList.contains("no-access")) {
- haveChild = true;
- }
- }
-
- if (button && haveChild) {
- button.classList.add("clickable");
- button.addEventListener("click", toggleResources);
- }
- }
- }
- makeClickableTitles();
-
- // show resources for every group in FPD report
- let showAll = (event) => {
- for (let element of document.querySelectorAll(".fpd-group > h4")) {
- element.classList.remove("hidden");
- }
- showBtn.classList.add("hidden");
- hideBtn.classList.remove("hidden");
- }
-
- // hide resources for every group in FPD report
- let hideDetails = (event) => {
- for (let element of document.querySelectorAll(".fpd-group > h4")) {
- element.classList.add("hidden");
- }
- showBtn.classList.remove("hidden");
- hideBtn.classList.add("hidden");
- }
-
- // show description/help for the report
- let showDescription = () => {
- for (let element of document.querySelectorAll(".description")) {
- element.classList.toggle("hidden");
- }
- }
-
- // show all groups/resources even if not accessed
- let showNotAccessed = () => {
- for (let element of document.querySelectorAll(".no-access")) {
- element.classList.remove("no-access");
- }
- makeClickableTitles();
- }
-
- // create on-site JSON representation of FPD evaluation data and download it
- function exportReport(filename) {
- let element = document.createElement("a");
- let obj = {
- fpd_evaluation_statistics: latestEvals.evalStats,
- fpd_access_logs: fpDb
- }
- element.setAttribute("href", "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj)));
- element.setAttribute("download", filename);
- element.style.display = "none";
- document.body.appendChild(element);
- element.click();
- document.body.removeChild(element);
- }
-
- document.getElementById("showBtn").addEventListener("click", showAll);
- document.getElementById("hideBtn").addEventListener("click", hideDetails);
- document.getElementById("exportBtn").addEventListener("click", exportReport.bind(null, `fpd_report_${url}.json`))
- document.getElementById("titletext").innerHTML += '<button id="help" class="help">?</button>';
- document.getElementById("help").addEventListener("click", showDescription);
- document.getElementById("unhideAll").addEventListener("click", showNotAccessed);
+ report.innerHTML = html;
+
+ // process generated document
+ let groupElements = document.querySelectorAll(".fpd-group.access");
+ for (let i = groupElements.length; i > 0; i--) {
+ // remove duplicit entries from groups
+ let duplicitArray = [];
+ let elements = groupElements[i-1].querySelectorAll(":scope > section > div.details > h4");
+ elements.forEach((d) => {
+ if (duplicitArray.indexOf(d.innerHTML) > -1) {
+ d.remove();
+ }
+ else {
+ duplicitArray.push(d.innerHTML);
+ }
+ });
+
+ // hide groups with no relevant entries
+ if (!document.querySelectorAll(`#${groupElements[i-1].id} > section > .access`).length) {
+ groupElements[i-1].classList.replace("access", "no-access");
+ }
+ }
+
+ // function that enables to show accessed resources of the group
+ function toggleResources(event) {
+ let parent = event.target.parentElement;
+ for (let i = 0; i < parent.children.length; i++) {
+ let child = parent.children[i];
+ if (child.tagName == "SECTION") {
+ child.classList.toggle("hidden");
+ }
+ }
+ }
+
+ // make group name clickable only if it makes sense - groups with resources
+ function makeGroupExpansionsClickable() {
+ for (let element of document.querySelectorAll(".fpd-group")) {
+ let button;
+
+ for (let i = 0; i < element.children.length; i++) {
+ let child = element.children[i];
+ if (child.tagName == "BUTTON") {
+ button = child;
+ }
+ }
+
+ if (button) {
+ button.classList.add("clickable");
+ button.addEventListener("click", toggleResources);
+ }
+ }
+ }
+ makeGroupExpansionsClickable();
+
+ // show resources for every group in FPD report
+ let showAll = (event) => {
+ for (let element of document.querySelectorAll(".fpd-group div.details")) {
+ element.classList.remove("hidden");
+ }
+ showBtn.classList.add("hidden");
+ hideBtn.classList.remove("hidden");
+ }
+
+ // hide resources for every group in FPD report
+ let hideDetails = (event) => {
+ for (let element of document.querySelectorAll(".fpd-group div.details")) {
+ element.classList.add("hidden");
+ }
+ showBtn.classList.remove("hidden");
+ hideBtn.classList.add("hidden");
+ }
+ hideDetails();
+
+ // refresh data in the report
+ function refreshReport() {
+ browser.runtime.sendMessage({
+ purpose: "fpd-get-report-data",
+ tabId: tabId
+ }).then((result) => {
+ createReport(result);
+ showAll();
+ });
+ browser.runtime.sendMessage({purpose: "fpd-track-callers-stop"});
+ document.getElementById("updateReportBtn").classList.remove("hidden");
+ document.getElementById("forgetCurrentBtn").classList.remove("hidden");
+ let trackCallersBtn = document.getElementById("trackCallersBtn");
+ trackCallersBtn.innerText = browser.i18n.getMessage("FPDReportTrackCallersRestart");
+ trackCallersBtn.classList.remove("hidden");
+ }
+
+ // Reload the report with data on the identity of the calling scripts
+ function trackCallers() {
+ tabId = new URLSearchParams(window.location.search).get("id");
+ function onReloaded() {
+ setTimeout(refreshReport, 5000);
+ report.innerHTML = browser.i18n.getMessage("FPDReportTrackCallersWaiting");
+ document.getElementById("trackCallersBtn").classList.add("hidden");
+ document.getElementById("updateReportBtn").classList.add("hidden");
+ document.getElementById("forgetCurrentBtn").classList.add("hidden");
+ }
+ function onError(error) {
+ document.getElementById("fpdError").innerHTML = browser.i18n.getMessage("FPDReportTrackCallersFailed", error);
+ browser.runtime.sendMessage({purpose: "fpd-track-callers-stop"});
+ }
+ browser.runtime.sendMessage({
+ purpose: "fpd-track-callers",
+ tabId: tabId
+ }).then(onReloaded, onError);
+ }
+
+ function updateReport() {
+ }
+
+ // show all groups/resources even if not accessed
+ let showNotAccessed = () => {
+ for (let element of document.querySelectorAll(".no-access")) {
+ element.classList.remove("no-access");
+ }
+ makeGroupExpansionsClickable();
+ }
+
+ function forgetTraces() {
+ for (resource of Object.values(fpDb)) {
+ for (type of Object.values(resource)) {
+ for (trace of Object.keys(type.callers)) {
+ hiddenTraces[trace] = true;
+ }
+ }
+ }
+ createReport(data);
+ }
+
+ // create on-site JSON representation of FPD evaluation data and download it
+ function exportReport(filename) {
+ let element = document.createElement("a");
+ let obj = {
+ fpd_evaluation_statistics: latestEvals.evalStats,
+ fpd_access_logs: fpDb
+ }
+ element.setAttribute("href", "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(obj)));
+ element.setAttribute("download", filename);
+ element.style.display = "none";
+ document.body.appendChild(element);
+ element.click();
+ document.body.removeChild(element);
+ }
+
+ document.getElementById("showBtn").onclick = showAll;
+ document.getElementById("hideBtn").onclick = hideDetails;
+ document.getElementById("exportBtn").onclick = exportReport.bind(null, `fpd_report_${url}.json`);
+ document.getElementById("trackCallersBtn").onclick = trackCallers;
+ document.getElementById("forgetCurrentBtn").onclick = forgetTraces;
+ document.getElementById("updateReportBtn").onclick = refreshReport;
+ document.getElementById("unhideAll").onclick = showNotAccessed;
+}
+
+// show description/help for the report
+let showDescription = () => {
+ for (let element of document.querySelectorAll(".description")) {
+ element.classList.toggle("hidden");
+ }
}
+
+window.addEventListener("DOMContentLoaded", function() {
+ document.getElementById("titletext").innerHTML += '<button id="help" class="help">?</button>';
+ document.getElementById("help").addEventListener("click", showDescription);
+});