diff --git a/background/api/classifier.js b/background/api/classifier.js
new file mode 100644
index 0000000..6d75ad7
--- /dev/null
+++ b/background/api/classifier.js
@@ -0,0 +1,13 @@
+import { ApiPost } from "./request";
+
+export const classifyText = (text, callback) => {
+ ApiPost('/classify/text', { text }, callback, () => {
+ console.error('api/calliope: Could not classify cookie banner text');
+ });
+};
+
+export const classifyImage = (imageData, callback) => {
+ ApiPost('/classify/image', { image_data: imageData }, callback, () => {
+ console.error('api/janus: Could not classify cookie banner screenshot');
+ });
+};
diff --git a/background/api/request.js b/background/api/request.js
new file mode 100644
index 0000000..2466381
--- /dev/null
+++ b/background/api/request.js
@@ -0,0 +1,35 @@
+const baseUrl = import.meta.env.VITE_API_URL + '/api/v1';
+
+export const ApiGet = (endpoint, callback, errorCallback) => {
+ if (typeof callback !== 'function') {
+ console.error('api/request(ApiGet): Expected function callback, got ' + typeof callback);
+ }
+
+ fetch(baseUrl + endpoint, {
+ method: 'GET'
+ }).then((response) => response.json())
+ .then((data) => callback(data))
+ .catch((error) => {
+ console.error('api/request(ApiGet) Error fetching ' + endpoint + ':', error);
+ if (typeof errorCallback === 'function') errorCallback(error);
+ });
+}
+
+export const ApiPost = (endpoint, body, callback, errorCallback) => {
+ if (typeof callback !== 'function') {
+ console.error('api/request(ApiPost): Expected function callback, got ' + typeof callback);
+ }
+
+ fetch(baseUrl + endpoint, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(body)
+ }).then((response) => response.json())
+ .then((data) => callback(data))
+ .catch((error) => {
+ console.error('api/request(ApiPost) Error fetching ' + endpoint + ':', error);
+ if (typeof errorCallback === 'function') errorCallback(error);
+ });
+}
diff --git a/background/api/stats.js b/background/api/stats.js
new file mode 100644
index 0000000..c17b461
--- /dev/null
+++ b/background/api/stats.js
@@ -0,0 +1,16 @@
+import { ApiPost } from './request';
+import { updateBadgeText } from '../browser/badge';
+import { setTransaction } from '../browser/storage';
+
+export const getStats = (tabUrl, successCallback, errorCallback) => {
+ ApiPost('/reports/by-url', {'page_url': tabUrl}, (data) => {
+ console.log('api/stats: Got stats for URL ' + tabUrl + ':', data);
+ setTransaction('stats', {
+ url: tabUrl,
+ stats: data
+ }).then(() => {
+ updateBadgeText(data);
+ if (successCallback !== undefined) successCallback(data);
+ }).catch((e) => errorCallback(e))
+ }, errorCallback);
+}
diff --git a/background/background.js b/background/background.js
index 91e8be2..83ce549 100644
--- a/background/background.js
+++ b/background/background.js
@@ -1,175 +1,10 @@
import * as browser from 'webextension-polyfill';
-
-class AriadneBackground {
- constructor() {
- this._tabStates = [];
- this._reportStats = {};
- this._API_URL = import.meta.env.VITE_API_URL + '/api/v1';
-
- // Alias if browser.action is defined, i.e. in Manifest V3
- this.BrowserAction = browser.browserAction;
- if (browser.action !== undefined) {
- this.BrowserAction = browser.action;
- }
- }
-
- addListeners() {
- // Update badge on tab change
- browser.tabs.onActivated.addListener((activeInfo) => {
- // Get URL of active tab
- browser.tabs.get(activeInfo.tabId)
- .then((tab) => {
- console.log('[bg] Tab changed to', tab.url);
- this.toggleBadge(this._tabStates[activeInfo.tabId]);
- this.updateBadgeText(this._reportStats[tab.url]);
- this.getStats(tab.url);
- });
- });
-
- // Tab URL change listener
- browser.tabs.onUpdated.addListener((tabId, changeInfo, _) => {
- if (changeInfo.url) {
- // Request fresh stats
- console.log('[bg] Tab ' + tabId + ' URL changed to', changeInfo.url);
- this.getStats(changeInfo.url);
- }
- });
-
- // Message listeners
- browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
- console.log("[bg] Received message with action", request.action);
- if (request.action === "updateBadge") {
- // Listen to updateBadge requests
- this.toggleBadge(request.args.enabled);
-
- // Update the tab state
- this._tabStates[sender.tab.id] = request.args.enabled;
- } else if (request.action === "detection") {
- // Listen to detection requests from content scripts
- const cookieBannerText = request.args.body;
- console.log('[bg] Detection request received from tab', sender.tab.id, 'with body:', cookieBannerText);
-
- // POST to API
- fetch(this._API_URL + '/classify/text', {
- method: 'POST',
- body: JSON.stringify({
- text: cookieBannerText
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then((response) => response.json())
- .then((data) => {
- console.log('[bg] Detection result from API:', data);
- sendResponse(data);
- });
- } else if (request.action === "visualDetection") {
- // Listen to visual detection requests from content scripts
- const imageData = request.args.screenshot;
- console.log('[bg] Visual detection request received from tab', sender.tab.id);
-
- // POST to API
- fetch(this._API_URL + '/classify/image', {
- method: 'POST',
- body: JSON.stringify({
- image_data: imageData
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then((response) => response.json())
- .then((data) => {
- console.log('[bg] Detection result from API:', data);
- sendResponse(data);
- });
- } else if (request.action === "requestStats") {
- console.log("[bg] Received stats request from popup", request, sender);
-
- // If we have cached stats, send them before requesting new ones
- const tabUrl = request.args.url;
- let deferSending = false;
- if (this._reportStats[tabUrl]) {
- console.log("[bg] Sending cached stats to tab", tabUrl, this._reportStats[tabUrl]);
- sendResponse(this._reportStats[tabUrl]);
- deferSending = true;
- }
-
- this.getStats(tabUrl, (stats) => {
- if (!deferSending) {
- console.log('[bg] Sending stats to tab', tabUrl, this._reportStats[tabUrl])
- sendResponse(stats);
- } else {
- console.log('[bg] Revalidated cache for tab', tabUrl, this._reportStats[tabUrl])
- }
- }, (error) => {
- sendResponse({
- success: false,
- error
- });
- });
- }
-
- return true;
- });
- }
-
- toggleBadge(state) {
- if (state) {
- this.BrowserAction.setBadgeBackgroundColor({
- color: "#B677FA",
- });
- } else {
- this.BrowserAction.setBadgeBackgroundColor({
- color: "#2A272A",
- });
- }
- }
-
- updateBadgeText(stats) {
- console.log('[bg] Updating badge text with stats:', stats);
- if (stats !== undefined && stats.hasOwnProperty("success") &&
- stats.hasOwnProperty("specific_reports") && stats["success"]) {
- const count = stats.specific_reports.count;
- console.log('[bg] Badge count:', count)
- if (count > 0) {
- this.BrowserAction.setBadgeText({
- text: count.toString(),
- });
- return;
- }
- }
- this.BrowserAction.setBadgeText({
- text: "0",
- });
- }
-
- getStats(tabUrl, successCallback, errorCallback) {
- fetch(this._API_URL + '/reports/by-url', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- 'page_url': tabUrl
- })
- })
- .then((response) => response.json())
- .then((data) => {
- console.log('[bg] Report stats from API:', data);
- this._reportStats[tabUrl] = data;
-
- // Update badge text
- this.updateBadgeText(data);
-
- if (successCallback !== undefined) successCallback(data);
- })
- .catch((error) => {
- console.error('[bg] Error fetching report stats:', error);
- if (errorCallback !== undefined) errorCallback(error);
- }
- );
- }
-}
-
-const ariadneBackground = new AriadneBackground();
-ariadneBackground.addListeners();
+import {
+ messageListener,
+ tabChangeListener,
+ tabUrlChangeListener
+} from './listeners';
+
+browser.runtime.onMessage.addListener(messageListener);
+browser.tabs.onActivated.addListener(tabChangeListener);
+browser.tabs.onUpdated.addListener(tabUrlChangeListener);
diff --git a/background/browser/badge.js b/background/browser/badge.js
new file mode 100644
index 0000000..b921dc7
--- /dev/null
+++ b/background/browser/badge.js
@@ -0,0 +1,33 @@
+import { getAction } from './getAction';
+
+const action = getAction();
+
+export const toggleBadge = (state) => {
+ if (state) {
+ action.setBadgeBackgroundColor({
+ color: "#B677FA",
+ });
+ } else {
+ action.setBadgeBackgroundColor({
+ color: "#2A272A",
+ });
+ }
+}
+
+export const updateBadgeText = (stats) => {
+ console.log('browser/badge: Updating badge text with stats:', stats);
+ if (stats !== undefined && stats.hasOwnProperty("success") &&
+ stats.hasOwnProperty("specific_reports") && stats["success"]) {
+ const count = stats.specific_reports.count;
+ console.log('browser/badge: Badge count:', count)
+ if (count > 0) {
+ action.setBadgeText({
+ text: count.toString(),
+ });
+ return;
+ }
+ }
+ action.setBadgeText({
+ text: "0",
+ });
+}
diff --git a/background/browser/getAction.js b/background/browser/getAction.js
new file mode 100644
index 0000000..59b0244
--- /dev/null
+++ b/background/browser/getAction.js
@@ -0,0 +1,8 @@
+import * as browser from 'webextension-polyfill';
+
+export const getAction = () => {
+ if (chrome.runtime.getManifest().manifest_version === 3) {
+ return browser.action;
+ }
+ return browser.browserAction;
+}
diff --git a/background/browser/storage.js b/background/browser/storage.js
new file mode 100644
index 0000000..36fb244
--- /dev/null
+++ b/background/browser/storage.js
@@ -0,0 +1,80 @@
+const openDatabase = () => new Promise((res, rej) => {
+ const request = indexedDB.open('ariadne', 1);
+ request.onerror = (event) => {
+ console.error(`browser/storage: Error ${event.target.errorCode}`)
+ rej();
+ };
+
+ // Declare schema
+ request.onupgradeneeded = (event) => {
+ const db = event.target.result;
+
+ // Create storage for stats per URL
+ const urlStatsStore = db.createObjectStore('stats', { keyPath: 'url' })
+ urlStatsStore.createIndex('url', 'url', { unique: true })
+
+ // Create storage for badge states per tab
+ const badgeStateStore = db.createObjectStore('badgeStates', { keyPath: 'tabId' })
+ badgeStateStore.createIndex('tabId', 'tabId', { unique: true })
+
+ // Create storage for Calliope results per URL
+ const calliopeStore = db.createObjectStore('calliope', { keyPath: 'url' })
+ calliopeStore.createIndex('url', 'url', { unique: true })
+
+ // Create storage for Janus results per URL
+ const janusStore = db.createObjectStore('janus', { keyPath: 'url' })
+ janusStore.createIndex('url', 'url', { unique: true })
+ }
+
+ request.onsuccess = (event) => {
+ console.log('browser/storage: Database ready');
+ res(event.target.result);
+ }
+});
+
+
+export const setTransaction = async (store, data) => {
+ console.log('browser/storage(set): Opening DB');
+
+ const db = await openDatabase();
+ const t = db.transaction(store, 'readwrite');
+ const s = t.objectStore(store);
+ return await new Promise((res, rej) => {
+ t.oncomplete = () => console.log('browser/storage(set): Complete');
+ t.onerror = (e) => rej(e);
+
+ const r = s.put(data);
+ r.onsuccess = () => res();
+ });
+}
+
+export const getTransaction = async (store, key) => {
+ console.log('browser/storage(get): Opening DB');
+
+ const db = await openDatabase();
+ const t = db.transaction(store, 'readonly');
+ const s = t.objectStore(store);
+ return await new Promise((res, rej) => {
+ t.oncomplete = () => console.log('browser/storage(get): Complete');
+ t.onerror = (ev) => rej(ev);
+
+ const r = s.get(key);
+ r.onsuccess = (e) => res(e.target.result);
+ });
+}
+
+export const deleteTransaction = async (store, key) => {
+ console.log('browser/storage(delete): Opening DB');
+
+ const db = await openDatabase();
+ const t = db.transaction(store, 'readwrite');
+ const s = t.objectStore(store);
+ return await new Promise((res, rej) => {
+ t.oncomplete = () => {
+ console.log('browser/storage(delete): Complete');
+ res();
+ };
+ t.onerror = (e) => rej(e);
+ s.delete(key);
+ });
+}
diff --git a/background/listeners/index.js b/background/listeners/index.js
new file mode 100644
index 0000000..7cdb280
--- /dev/null
+++ b/background/listeners/index.js
@@ -0,0 +1,9 @@
+import messageListener from './message';
+import tabChangeListener from './tabChange';
+import tabUrlChangeListener from './tabUrlChange';
+
+export {
+ messageListener,
+ tabChangeListener,
+ tabUrlChangeListener
+}
diff --git a/background/listeners/message.js b/background/listeners/message.js
new file mode 100644
index 0000000..7d2d482
--- /dev/null
+++ b/background/listeners/message.js
@@ -0,0 +1,108 @@
+import { toggleBadge } from "../browser/badge";
+import { getTransaction, setTransaction } from "../browser/storage";
+import { getStats } from "../api/stats";
+import { classifyImage, classifyText } from "../api/classifier";
+import * as browser from "webextension-polyfill";
+
+export default (request, sender, sendResponse) => {
+ if (request.action === "updateBadge") {
+ // Save and apply the badge state
+ setTransaction('badgeStates', {
+ tabId: sender.tab.id,
+ enabled: request.args.enabled
+ }).then(() => toggleBadge(request.args.enabled));
+ } else if (request.action === "detection") {
+ // Listen to detection requests from content scripts
+ const cookieBannerText = request.args.body;
+ console.log('listeners/message: Calliope request received from tab', sender.tab.id, 'with body:', cookieBannerText);
+
+ // POST to API
+ classifyText(cookieBannerText, (data) => {
+ console.log('listeners/message: Calliope result from API:', data);
+
+ // Save result
+ browser.tabs.get(sender.tab.id)
+ .then((tab) => tab.url)
+ .then((url) => setTransaction('calliope', {
+ url,
+ cookieBannerText,
+ tripped: !data.is_good
+ }))
+ .then(() => sendResponse(data));
+ });
+ } else if (request.action === "visualDetection") {
+ // Listen to visual detection requests from content scripts
+ const imageData = request.args.screenshot;
+ console.log('listeners/message: Janus request received from tab', sender.tab.id);
+
+ // POST to API
+ classifyImage(imageData, (data) => {
+ console.log('listeners/message: Janus result from API:', data);
+
+ // Save result
+ browser.tabs.get(sender.tab.id)
+ .then((tab) => tab.url)
+ .then((url) => setTransaction('janus', {
+ url,
+ imageData,
+ result: data.classification
+ }))
+ .then(() => sendResponse(data));
+ });
+ } else if (request.action === "requestStats") {
+ console.log("listeners/message: Received stats request from popup", request, sender);
+
+ (async () => {
+ const tabUrl = request.args.url;
+ let calliopeResult = '';
+ let janusResult = '';
+ let cookieBannerText = '';
+ let imageData = '';
+
+ // Try fetching results from DB
+ try {
+ const cachedCalliope = await getTransaction('calliope', tabUrl);
+ calliopeResult = JSON.stringify(cachedCalliope.tripped);
+ cookieBannerText = cachedCalliope.cookieBannerText;
+ } catch (_) {
+ // do nothing
+ }
+ try {
+ const cachedJanus = await getTransaction('janus', tabUrl);
+ janusResult = cachedJanus.result;
+ imageData = cachedJanus.imageData;
+ } catch (_) {
+ // do nothing
+ }
+
+ // If we have cached stats, send them before requesting new ones
+ let deferSending = false;
+ let stats = { calliopeResult, janusResult, cookieBannerText, imageData };
+ try {
+ stats = { ...stats, ...await getTransaction('stats', tabUrl) };
+ console.log('listeners/message: Sending stats to tab', tabUrl, stats)
+ sendResponse(stats);
+ deferSending = true;
+ } catch (e) {
+ console.error(e);
+ } finally {
+ getStats(tabUrl, (newStats) => {
+ stats = { ...stats, ...newStats };
+ if (!deferSending) {
+ console.log('listeners/message: Sending stats to tab', tabUrl, stats)
+ sendResponse(stats);
+ } else {
+ console.log('listeners/message: Revalidated cache for tab', tabUrl)
+ }
+ }, (error) => {
+ sendResponse({
+ success: false,
+ error
+ });
+ })
+ }
+ })();
+ }
+
+ return true;
+}
diff --git a/background/listeners/tabChange.js b/background/listeners/tabChange.js
new file mode 100644
index 0000000..f562b87
--- /dev/null
+++ b/background/listeners/tabChange.js
@@ -0,0 +1,29 @@
+import * as browser from 'webextension-polyfill';
+import { toggleBadge, updateBadgeText } from "../browser/badge";
+import { getStats } from "../api/stats";
+import { getTransaction } from '../browser/storage';
+
+export default (activeInfo) => {
+ // Get URL of active tab
+ browser.tabs.get(activeInfo.tabId)
+ .then((tab) => {
+ console.log('listeners/tabChange: Tab changed to', tab.url);
+
+ // Fetch latest stats
+ return new Promise((res, rej) => {
+ getStats(tab.url, () => res(tab.url), rej);
+ })
+ })
+ .then(async (url) => {
+ console.log('listeners/tabChange: Stats refreshed');
+ let enabled = false;
+ try {
+ enabled = (await getTransaction('badgeStates', activeInfo.tabId)).enabled;
+ } catch (_) {
+ // do nothing
+ }
+ const { stats } = await getTransaction('stats', url);
+ toggleBadge(enabled);
+ updateBadgeText(stats);
+ });
+}
diff --git a/background/listeners/tabUrlChange.js b/background/listeners/tabUrlChange.js
new file mode 100644
index 0000000..f4bce50
--- /dev/null
+++ b/background/listeners/tabUrlChange.js
@@ -0,0 +1,9 @@
+import { getStats } from "../api/stats";
+
+export default (tabId, changeInfo, _) => {
+ if (changeInfo.url) {
+ // Request fresh stats
+ console.log('listeners/tabUrlChange: Tab ' + tabId + ' changed to', changeInfo.url);
+ getStats(changeInfo.url);
+ }
+}
diff --git a/content/content.js b/content/content.js
index 3218f95..9564858 100644
--- a/content/content.js
+++ b/content/content.js
@@ -1,6 +1,28 @@
import html2canvas from 'html2canvas';
import * as browser from 'webextension-polyfill';
+function canvasHandler(canvas) {
+ const screenshot = canvas.toDataURL("image/png");
+ console.log("[content] Screenshot taken, sending to service worker");
+
+ // Send screenshot to service worker for detection
+ browser.runtime.sendMessage({
+ action: "visualDetection",
+ args: { screenshot }
+ })
+ .then((response) => {
+ console.log("[content] Result from Janus API:", response);
+
+ // Update badge
+ return browser.runtime.sendMessage({
+ action: "updateBadge",
+ args: {
+ enabled: response["classification"] == "weighted"
+ }
+ });
+ });
+}
+
let bannerDiv = null;
function performDetection() {
// Look for floating divs, i.e. divs with position: fixed|absolute|sticky
@@ -43,42 +65,17 @@ function performDetection() {
.then((response) => {
console.log("[content] Badge updated:", response);
});
+
break;
}
}
- // No banner found
- if (bannerDiv === null) {
- console.log("[content] No banner found");
-
- // Take screenshot of viewport
- html2canvas(document.body, {
- x: window.scrollX,
- y: window.scrollY,
- width: window.innerWidth,
- height: window.innerHeight,
- }).then((canvas) => {
- const screenshot = canvas.toDataURL("image/png");
- console.log("[content] Screenshot taken, sending to service worker");
-
- // Send screenshot to service worker for detection
- browser.runtime.sendMessage({
- action: "visualDetection",
- args: { screenshot }
- })
- .then((response) => {
- console.log("[content] Result from Janus API:", response);
-
- // Update badge
- return browser.runtime.sendMessage({
- action: "updateBadge",
- args: {
- enabled: response["classification"] == "weighted"
- }
- });
- });
- });
- return;
+ // Perform visual detection
+ if (bannerDiv !== null) {
+ console.log("[content] Taking screenshot of banner");
+ html2canvas(bannerDiv).then(canvasHandler);
+ } else {
+ console.log("[content] No banner div found, skipping visual detection");
}
}
diff --git a/manifest/v2.json b/manifest/v2.json
index 1c8b575..43c851d 100644
--- a/manifest/v2.json
+++ b/manifest/v2.json
@@ -3,7 +3,7 @@
"short_name": "__MSG_appShortName__",
"description": "__MSG_appDescription__",
"homepage_url": "https://ariadne.dantis.me",
- "version": "0.1.0",
+ "version": "0.1.1",
"manifest_version": 2,
"default_locale": "en",
"permissions": [
@@ -30,7 +30,7 @@
},
"content_scripts": [
{
- "js": ["./content.global.js"],
+ "js": ["./content.js"],
"matches": ["http://*/*", "https://*/*"],
"run_at": "document_idle"
}
diff --git a/manifest/v3.json b/manifest/v3.json
index 2b04971..f4c4790 100644
--- a/manifest/v3.json
+++ b/manifest/v3.json
@@ -3,8 +3,8 @@
"short_name": "__MSG_appShortName__",
"description": "__MSG_appDescription__",
"homepage_url": "https://ariadne.dantis.me",
- "version": "0.1.0",
- "version_name": "0.1.0",
+ "version": "0.1.1",
+ "version_name": "0.1.1",
"manifest_version": 3,
"default_locale": "en",
"minimum_chrome_version": "88",
@@ -15,7 +15,7 @@
"128": "assets/img/Button@8x.png"
},
"background": {
- "service_worker": "./background.global.js"
+ "service_worker": "./background.js"
},
"action": {
"default_icon": {
@@ -28,7 +28,7 @@
},
"content_scripts": [
{
- "js": ["./content.global.js"],
+ "js": ["./content.js"],
"matches": ["http://*/*", "https://*/*"],
"run_at": "document_idle"
}
diff --git a/package-lock.json b/package-lock.json
index 569bc47..3c750c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,15 @@
{
"name": "ariadne",
- "version": "0.0.1",
+ "version": "0.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ariadne",
- "version": "0.0.1",
+ "version": "0.1.1",
"dependencies": {
"@heroicons/vue": "^2.0.16",
+ "flowbite": "^1.6.5",
"html2canvas": "^1.4.1",
"pinia": "^2.0.32",
"vue": "^3.2.47",
@@ -809,6 +810,15 @@
"node": ">=12"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.7",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
+ "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@sindresorhus/is": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
@@ -3058,6 +3068,15 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
+ "node_modules/flowbite": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-1.6.5.tgz",
+ "integrity": "sha512-eI4h3pIRI9d7grlYq14r0A01KUtw7189sPLLx/O2i7JyPEWpbleScfYuEc48XTeNjk1xxm/JHgZkD9kjyOWAlA==",
+ "dependencies": {
+ "@popperjs/core": "^2.9.3",
+ "mini-svg-data-uri": "^1.4.3"
+ }
+ },
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -4348,6 +4367,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+ "bin": {
+ "mini-svg-data-uri": "cli.js"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -7483,6 +7510,11 @@
"config-chain": "^1.1.11"
}
},
+ "@popperjs/core": {
+ "version": "2.11.7",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
+ "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
+ },
"@sindresorhus/is": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
@@ -9142,6 +9174,15 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
+ "flowbite": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-1.6.5.tgz",
+ "integrity": "sha512-eI4h3pIRI9d7grlYq14r0A01KUtw7189sPLLx/O2i7JyPEWpbleScfYuEc48XTeNjk1xxm/JHgZkD9kjyOWAlA==",
+ "requires": {
+ "@popperjs/core": "^2.9.3",
+ "mini-svg-data-uri": "^1.4.3"
+ }
+ },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -10126,6 +10167,11 @@
"integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
"dev": true
},
+ "mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="
+ },
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
diff --git a/package.json b/package.json
index 041fef7..6c38460 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ariadne",
"description": "Detect deceptive design on the Web",
- "version": "0.1.0",
+ "version": "0.1.1",
"homepage": "https://ariadne.dantis.me",
"author": "Juris Hannah Adorna & Aurel Jared Dantis",
"repository": {
@@ -24,6 +24,7 @@
},
"dependencies": {
"@heroicons/vue": "^2.0.16",
+ "flowbite": "^1.6.5",
"html2canvas": "^1.4.1",
"pinia": "^2.0.32",
"vue": "^3.2.47",
diff --git a/src/components/Checkbox.vue b/src/components/Checkbox.vue
index 0d4b61a..1c198a6 100644
--- a/src/components/Checkbox.vue
+++ b/src/components/Checkbox.vue
@@ -7,10 +7,12 @@ import { MinusIcon, PlusIcon } from '@heroicons/vue/24/outline'
@click="handleChange"
class="group rounded-lg box-border border-2
px-4 py-3 flex flex-row justify-between items-start
- transition-colors cursor-pointer select-none"
+ transition-colors select-none"
:class="{
'bg-adn-dark border-adn-dark': isChecked,
- 'bg-white border-adn-border': !isChecked
+ 'bg-white border-adn-border': !isChecked,
+ 'cursor-not-allowed opacity-30': disabled,
+ 'cursor-pointer': !disabled
}"
>
@@ -42,11 +44,16 @@ export default defineComponent({
checkboxKey: {
type: String,
required: true
+ },
+ disabled: {
+ type: Boolean,
+ default: false
}
},
emits: ['update:value'],
methods: {
handleChange(e) {
+ if (this.disabled) { return }
this.isChecked = !this.isChecked
this.$emit('update:value', {
key: this.checkboxKey,
diff --git a/src/components/Header.vue b/src/components/Header.vue
index 1d92d1b..db50dec 100644
--- a/src/components/Header.vue
+++ b/src/components/Header.vue
@@ -31,7 +31,7 @@ import { Cog6ToothIcon } from '@heroicons/vue/24/outline'
+
-->
ALPHA
diff --git a/src/components/PillCount.vue b/src/components/PillCount.vue
index 02f5396..a25eea3 100644
--- a/src/components/PillCount.vue
+++ b/src/components/PillCount.vue
@@ -2,7 +2,7 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
index f1c6690..7fdd872 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -17,15 +17,22 @@ const router = createRouter({
},
},
{
- path: '/report-positive',
- name: 'report-positive',
+ path: '/report-manual',
+ name: 'report-manual',
meta: { title: 'Report Deceptive Design' },
- // route level code-splitting
- // this generates a separate chunk (About.[hash].js) for this route
- // which is lazy-loaded when the route is visited.
- component: () => import('../views/ReportPositiveView.vue')
+ component: () => import('../views/ManualReportView.vue')
+ },
+ {
+ path: '/report-auto',
+ name: 'report-auto',
+ meta: { title: 'Submit Detection Feedback' },
+ component: () => import('../views/AutoReportView.vue')
}
- ]
+ ],
+ scrollBehavior(to, from, savedPosition) {
+ // always scroll to top
+ return { top: 0 }
+ },
})
export default router
diff --git a/src/stores/ariadne.js b/src/stores/ariadne.js
index dc3cacc..e9dc815 100644
--- a/src/stores/ariadne.js
+++ b/src/stores/ariadne.js
@@ -6,7 +6,11 @@ export const useAriadneStore = defineStore('ariadne', {
currentFavicon: '',
currentPath: '',
currentURL: '',
- isRunningInExtension: false
+ isRunningInExtension: false,
+ calliopeTripped: '',
+ calliopeText: '',
+ janusResult: '',
+ janusScreenshot: ''
}),
actions: {
setDomain(domain) {
@@ -23,6 +27,12 @@ export const useAriadneStore = defineStore('ariadne', {
},
setRunningInExtension(isRunningInExtension) {
this.isRunningInExtension = isRunningInExtension;
+ },
+ setDetectionData({ calliopeTripped, calliopeText, janusResult, janusScreenshot }) {
+ this.calliopeTripped = calliopeTripped;
+ this.calliopeText = calliopeText;
+ this.janusResult = janusResult;
+ this.janusScreenshot = janusScreenshot;
}
}
})
diff --git a/src/views/AutoReportView.vue b/src/views/AutoReportView.vue
new file mode 100644
index 0000000..98d8f2a
--- /dev/null
+++ b/src/views/AutoReportView.vue
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+ Submit detection feedback
+
+
+ Domain
+ {{ store.currentDomain }}
+ Path
+ {{ store.currentPath }}
+
+
+ Calliope result
+ Likely {{ calliopeResult }}
+ Janus result
+ Likely {{ store.janusResult || 'no banner present' }}
+ Reporting as
+ {{ reportType }} detection
+
+
+
+
+
Attachments
+
+ You can choose to include the items below in your report by clicking them.
+ This will help us improve Ariadne in the future.
+
+
+ Text content
+ {{ store.calliopeText }}
+
+
+ Screenshot
+
+
+
+
+
+
+ As the extracted cookie banner text and screenshot may contain personal
+ information, Ariadne will only submit them if you explicitly allow it to do so.
+
+
+
+
+
+ By clicking Submit, you allow Ariadne to submit a report to the Dionysus server
+ at {{ dionysusUrl }} containing information about this page,
+ along with any attachments and remarks you have provided.
+
+ Click here to view our privacy policy.
+
+
+
+
+
+
+
+ Submit
+
+
+
+
+ Cancel
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 3f56804..69858bb 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -24,6 +24,73 @@
+
+
+
+
+
{{ tripped ? 'D' : 'No d' }}eceptive design
detected here
+
+
+
+
+
+
+
+
+
+
+
+
+ Report correct detection
+
+
+
+
+
+
+
+
+
+
+
+ Report incorrect detection
+
+
+
+
+
+ The cookie banner on this page might be making use of
+ {{ trippedText }}.
+
+
+ The cookie banner on this page, if any, is not making use of deceptive design.
+
+
+
@@ -64,27 +131,23 @@
-
-
- There is deceptive design on this page
+
+
+ Report deceptive design on this page
-
-
-
- No deceptive design on this page
-