diff --git a/api/cogniguard/manage.py b/api/cogniguard/manage.py new file mode 100644 index 0000000..d4132bd --- /dev/null +++ b/api/cogniguard/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cogniguard.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/api/cogniguard/settings.py b/api/cogniguard/settings.py index 8906834..2d10228 100644 --- a/api/cogniguard/settings.py +++ b/api/cogniguard/settings.py @@ -35,6 +35,7 @@ INSTALLED_APPS = [ 'mlApi.apps.MlapiConfig', 'home.apps.HomeConfig', + 'rest_framework', 'corsheaders', 'django.contrib.admin', @@ -76,6 +77,8 @@ WSGI_APPLICATION = 'cogniguard.wsgi.application' +CORS_ALLOW_ALL_ORIGINS = True + # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases @@ -136,7 +139,7 @@ ] -CORS_ALLOWED_ORIGINS = [ - "chrome-extension://fbaobglhjfffocnidihaombgpjhcpail", - "http://127.0.0.1:5500", -] +# CORS_ORIGIN_WHITELIST = [ +# "chrome-extension://fbaobglhjfffocnidihaombgpjhcpail", +# "http://127.0.0.1:8000", +# ] diff --git a/api/home/keras_model.h5 b/api/home/keras_model.h5 new file mode 100644 index 0000000..39b9295 Binary files /dev/null and b/api/home/keras_model.h5 differ diff --git a/api/home/labels.txt b/api/home/labels.txt new file mode 100644 index 0000000..71fa558 --- /dev/null +++ b/api/home/labels.txt @@ -0,0 +1,3 @@ +0 Not Popup +1 Popup +2 Side Popup diff --git a/api/home/popup_detect_ml.py b/api/home/popup_detect_ml.py new file mode 100644 index 0000000..90b235d --- /dev/null +++ b/api/home/popup_detect_ml.py @@ -0,0 +1,47 @@ +from keras.models import load_model # TensorFlow is required for Keras to work +from PIL import Image, ImageOps #TODO: Install pillow instead of PIL +import numpy as np + + +def predict(img): + # Disable scientific notation for clarity + np.set_printoptions(suppress=True) + + # Load the model + model = load_model("keras_Model.h5", compile=False) + + # Load the labels + class_names = open("labels.txt", "r").readlines() + + # Create the array of the right shape to feed into the keras model + # The 'length' or number of images you can put into the array is + # determined by the first position in the shape tuple, in this case 1 + data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32) + + + image = img.convert("RGB") + + # resizing the image to be at least 224x224 and then cropping from the center + size = (224, 224) + image = ImageOps.fit(image, size, Image.Resampling.LANCZOS) + + # turn the image into a numpy array + image_array = np.asarray(image) + + # Normalize the image + normalized_image_array = (image_array.astype(np.float32) / 127.5) - 1 + + # Load the image into the array + data[0] = normalized_image_array + + # Predicts the model + prediction = model.predict(data) + index = np.argmax(prediction) + class_name = class_names[index] + confidence_score = prediction[0][index] + + # Print prediction and confidence score + + return [class_name[2:],confidence_score] + # print("Class:", class_name[2:], end="") + # print("Confidence Score:", confidence_score) diff --git a/api/home/urls.py b/api/home/urls.py index 8b980b3..13cd79a 100644 --- a/api/home/urls.py +++ b/api/home/urls.py @@ -5,6 +5,9 @@ urlpatterns = [ path("", views.index, name="home"), path("faqs/", views.faqs, name="faqs"), + + path("popup_detect/", views.popup_detect, name="popup_detect"), path("report-dp/", views.reportDp, name="report-dp"), - + ] + diff --git a/api/home/views.py b/api/home/views.py index f82719c..14fa465 100644 --- a/api/home/views.py +++ b/api/home/views.py @@ -1,4 +1,10 @@ from django.shortcuts import render,redirect +from .popup_detect_ml import predict +from django.http import JsonResponse +from json import dump +import io +import base64 +from PIL import Image # Create your views here. @@ -8,6 +14,42 @@ def index(request): def faqs(request): return render(request, "faqs.html") + +def popup_detect(request): + # img = data_url_to_image(request.GET.get('img', '')) + # return JsonResponse(dump(predict(img))) + + #get image data url + img = request.GET.get('img', '') + + if(img == ""): + return JsonResponse({"error" : "data url empty"}) + + #convert dataurl to PIL + img=data_url_to_image(img) + + #predict the result using trained ml algo + result = predict(img) + + # return the results + return JsonResponse(dump(result)) + + +def data_url_to_image(data_url): + # Remove the header of the data URL + header, base64_str = data_url.split(',') + + # Decode the base64 string to bytes + decoded_image = base64.b64decode(base64_str) + + # Create a BytesIO object and read the decoded image into it + image_data = io.BytesIO(decoded_image) + + # Open the image with PIL + img = Image.open(image_data) + + return img + def reportDp(request): return render(request, "report.html") diff --git a/cognigaurd-web/background.js b/cognigaurd-web/background.js index 2d98db3..f43a61d 100644 --- a/cognigaurd-web/background.js +++ b/cognigaurd-web/background.js @@ -1,10 +1,129 @@ -chrome.runtime.onMessage.addListener( - function(request, sender, sendResponse) { - if (request.message === "open_popup") { - chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { - chrome.tabs.sendMessage(tabs[0].id, {"message": "open_popup"}); - }); +const fs = require('fs'); +const sendWebsiteData = (dat) => { + const websiteData = { + img: dat, + // Add more data as needed + }; + + newapiUrl="http://127.0.0.1:8000/" + + console.log(websiteData); + + // Send data to API + fetch(newapiUrl +"popup_detect/", { + + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(websiteData), + }) + .then(response => { + if (!response.ok) { + throw new Error("Error sending website data to API"); + } + return response.json(); + }) + .then(apiResponse => { + console.log("API Response:", apiResponse); + }) + .catch(error => { + console.error("Error:", error); + }); +}; + +chrome.tabs.onUpdated.addListener(function(tabId, changeInfo,tab) { + if (changeInfo.status == 'complete' && !tab.url.startsWith('chrome://')) { + chrome.scripting.executeScript({ + target: { tabId: tabId }, + files: ['popup_detectV2.js'] + }); + } +}); + +let lastMessageTime = Date.now(); + + +chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { + if (request.message === "open_popup") { + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + chrome.tabs.sendMessage(tabs[0].id, {"message": "open_popup"}); + }); + } + + const currentTime = Date.now(); + + + if (currentTime - lastMessageTime < 3000) { + lastMessageTime = currentTime; + return; // Ignore the message if it's within 3 second of the previous message + } + + lastMessageTime = currentTime; + + if (request.message == "center popup" || request.message == "side popup") { + const tabId = sender.tab.id; + + chrome.tabs.captureVisibleTab(null, {}, function (dataUrl){ + console.log('Popup detected on ' + sender.tab.url); + console.log(typeof(dataUrl)); + sendWebsiteData(dataUrl); + }); + + // console.log('Popup count for tab ' + tabId + ': ' + popup_cnt[tabId]); + } else { + console.log(request.message); + sendWebsiteData(dataUrl); + } +}); + +// Capture screenshot on click +chrome.tabs.onActivated.addListener(function(activeInfo) { + chrome.tabs.captureVisibleTab(null, {}, function (dataUrl){ + // console.log('Screenshot captured on ' + tab.url); + // console.log(typeof(dataUrl)); + sendWebsiteData(dataUrl); + + // Write data URL to file + fs.writeFile('ss_scrape.txt', dataUrl, function(err) { + if (err) { + console.error('Error writing data URL to file:', err); + } else { + console.log('Data URL written to ss_scrape.txt'); } - } - ); - \ No newline at end of file + }); + }); +}); + +// function convertDataUrlToRgb(dataUrl) { +// const canvas = document.createElement('canvas'); +// const context = canvas.getContext('2d'); +// const image = new Image(); + +// return new Promise((resolve, reject) => { +// image.onload = function() { +// canvas.width = image.width; +// canvas.height = image.height; +// context.drawImage(image, 0, 0); + +// const imageData = context.getImageData(0, 0, canvas.width, canvas.height); +// const pixels = imageData.data; + +// const rgbData = []; +// for (let i = 0; i < pixels.length; i += 4) { +// const r = pixels[i]; +// const g = pixels[i + 1]; +// const b = pixels[i + 2]; +// rgbData.push([r, g, b]); +// } + +// resolve(rgbData); +// }; + +// image.onerror = function() { +// reject(new Error('Failed to load image')); +// }; + +// image.src = dataUrl; +// }); +// } diff --git a/cognigaurd-web/manifest.json b/cognigaurd-web/manifest.json index a3cbf78..13f32e4 100644 --- a/cognigaurd-web/manifest.json +++ b/cognigaurd-web/manifest.json @@ -1,53 +1,53 @@ { - "manifest_version": 3, - "name": "CogniGuard", - "version": "0.1.0", - "description": "Detects dark patterns on websites.", - "permissions": [ - "activeTab", - "storage", - "tabs", - "notifications" - - ], - "host_permissions": [ - "https://*/*", - "http://*/*" - ], - - "action": { - "default_icon": { - "16": "assets/images/icon-black.png", - "24": "assets/images/icon-black.png", - "32": "assets/images/icon-black.png" - }, - "default_title": "CogniGuard", - "default_popup": "popup.html" + "manifest_version": 3, + "name": "CogniGuard", + "version": "0.1.0", + "description": "Detects dark patterns on websites.", + "permissions": [ + "activeTab", + "storage", + "tabs", + "scripting", + "notifications", + "activeTab" + ], + "host_permissions": [ + "https://*/*", + "http://*/*" + ], + + "action": { + "default_icon": { + "16": "assets/images/icon-black.png", + "24": "assets/images/icon-black.png", + "32": "assets/images/icon-black.png" }, + "default_title": "CogniGuard", + "default_popup": "popup.html" + }, - "background": { - "service_worker": "background.js" - }, + "background": { + "service_worker": "background.js" + }, - - "content_scripts": [ - { - "matches": [""], - "js": ["content.js"] - } - ], - "web_accessible_resources": [ - { - "resources": [ - "assets/images/icon-black.png", - "assets/images/fg-scan.jpg" - ], + + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"] + } + ], + "web_accessible_resources": [ + { + "resources": [ + "assets/images/icon-black.png", + "assets/images/fg-scan.jpg" + ], - "matches": [""] - } + "matches": [""] + } - ] + ] - - } - \ No newline at end of file + +} diff --git a/cognigaurd-web/pop_detect_redacted.js b/cognigaurd-web/pop_detect_redacted.js new file mode 100644 index 0000000..1c95a40 --- /dev/null +++ b/cognigaurd-web/pop_detect_redacted.js @@ -0,0 +1,30 @@ +observers=new IntersectionObserver(function(entries){ + entries.forEach(function(entry){ + // console.log(entry); + if(entry.isIntersecting){ + if(Math.abs(entry.boundingClientRect.left+(entry.boundingClientRect.width/2)- window.innerWidth/2) < 15 && Math.abs(entry.boundingClientRect.top+(entry.boundingClientRect.height/2) - window.innerHeight/2) < 15){ + // console.log(entry) + } + } + }); + },{threshold:1}); + + target=null; + document.querySelectorAll("*").forEach(function(target){ + observers.observe(target); + }); + + + console.log(document.getElementsByClassName("grid grid-cols-7 md:grid-cols-12 mx-auto h-full relative").getboundingClientRect); + + // once finalizing a popup element eliminate the intersectionobserver then just observe it to reduce computataion + // add more filters to prevent false positive + // like : keyword matching in text + /* + z index + centering + close button + modal + background opacity + + */ \ No newline at end of file diff --git a/cognigaurd-web/popup_detectV2.js b/cognigaurd-web/popup_detectV2.js new file mode 100644 index 0000000..e9d871d --- /dev/null +++ b/cognigaurd-web/popup_detectV2.js @@ -0,0 +1,44 @@ +prev_node = ""; + +observer = new MutationObserver(function (mutations) { + mutations.forEach((mutation) => { + if (mutation.attributeName === "style") { + try { + let elems = document.querySelectorAll("." + mutation.target.className); + + if (prev_node === mutation.target.className) return; + + if (elems.length <= 30) { + Array.from(elems).forEach((elem) => { + + temp = elem.getBoundingClientRect(); + + if (Math.abs(temp.left + temp.width / 2 - window.innerWidth / 2) <30 && Math.abs(temp.top + temp.height / 2 - window.innerHeight / 2) < 30) { + chrome.runtime.sendMessage({ + message: "center popup" + }); + return; + } + + else { + chrome.runtime.sendMessage({ + message: "side popup" + }); + + return; + } + }); + } + } + + catch (err) {} + prev_node = mutation.target.className; + } + }); +}); + +observer.observe(document, { + attributes: true, + childList: true, + subtree: true, +});