From a93f958fc8e3d04ece9dba9eff9197c02b238608 Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Mon, 4 Mar 2024 09:38:12 +0100 Subject: [PATCH 01/13] Update README.md --- README.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 69dd00a..6b3e097 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,14 @@ Follow these steps to get started: Launch BugHog using the following command: ```bash -docker compose up +docker compose up -d ``` > :warning: If you use `sudo` with this command, the `PWD` environment variable won't be passed to the BugHog containers, which is necessary for dynamically starting worker containers. > To avoid this, explicitly pass on this variable: `sudo PWD=$PWD docker compose up`. Open your web browser and navigate to [http://localhost:5000](http://localhost:5000) to access the graphical interface. -BugHog is started on a remote server, substitute 'localhost' with its IP address. +If BugHog is started on a remote server, substitute 'localhost' with the appropriate IP address. BugHog can be stopped through: ```bash @@ -98,7 +98,7 @@ Be sure to restart the BugHog framework when you add a new experiment: ```bash docker compose down -docker compose up +docker compose up -d ``` ## Development @@ -114,13 +114,26 @@ For debugging the core application, consider using the VS Code dev container. You can utilize the configuration in [.devcontainer](.devcontainer) for this. -## Additional help +## Contact -Don't hesitate to open a [GitHub issue](https://github.com/DistriNet/BugHog/issues/new) if you come across a bug, want to suggest a feature, or have any questions! +Don't hesitate to open a [GitHub issue](https://github.com/DistriNet/BugHog/issues/new) if you encounter a bug or want to suggest a feature! + +For questions or collaboration, you can reach out to [Gertjan Franken](https://distrinet.cs.kuleuven.be/people/GertjanFranken). ## Troubleshooting +If something isn't working as expected, check out the troubleshooting tips below. +If you don't find a solution, don't hesitate to open a [GitHub issue](https://github.com/DistriNet/BugHog/issues/new). +Feel free to include any relevant logs. + + +### Consult the logs + +- Try launching BugHog without the `-d` flag to see logging output in the terminal, which might provide more information about the issue. +- For more detailed logs at the `DEBUG` level, check out the [logs](/logs) folder for all logging files. + + ### WSL on Windows - Ensure you clone the BugHog project to the WSL file system instead of the Windows file system, and launch it from there. From 8efbb4c8c5e5a6d3f92fa1a503c002b17b5e3f4f Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Mon, 18 Mar 2024 16:36:31 +0000 Subject: [PATCH 02/13] Update Docker config for debugging --- .dockerignore | 2 ++ Dockerfile | 4 ++-- .../resolver/symlinked/_/symlink_target/.gitkeep | 0 .../tailwindcss/types/generated/.gitkeep | 0 docker-compose.yml | 13 +++++++++---- 5 files changed, 13 insertions(+), 6 deletions(-) delete mode 100644 bci/ui/frontend/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep delete mode 100644 bci/ui/frontend/node_modules/tailwindcss/types/generated/.gitkeep diff --git a/.dockerignore b/.dockerignore index ae2875b..fd5fbff 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,6 @@ **/__pycache__ +**/dist +**/node_modules .env .flake8 .git diff --git a/Dockerfile b/Dockerfile index a872109..9859532 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,8 @@ FROM python:3.11-slim-buster AS base WORKDIR /app RUN apt-get update -y -RUN apt install -y curl gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils libgbm-dev xvfb dbus-x11 libnss3-tools python3-pip vim multiarch-support wget git procps \ - && rm -rf /var/lib/apt/lists/* +RUN apt install -y curl gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils libgbm-dev xvfb dbus-x11 libnss3-tools python3-pip vim multiarch-support wget git procps &&\ + rm -rf /var/lib/apt/lists/* RUN curl -sSL https://get.docker.com/ | sh diff --git a/bci/ui/frontend/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep b/bci/ui/frontend/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bci/ui/frontend/node_modules/tailwindcss/types/generated/.gitkeep b/bci/ui/frontend/node_modules/tailwindcss/types/generated/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.yml b/docker-compose.yml index 1e53f6a..75e482c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -115,13 +115,18 @@ services: #================# node_dev: - image: node:lts-alpine + build: + context: . + target: ui-build-stage ports: - "5173:5173" volumes: - - ./bci/ui/frontend:/app - working_dir: /app - entrypoint: ["npm", "run", "dev", "--", "--host"] + - ./bci/ui/frontend/public:/app/public:rw + - ./bci/ui/frontend/src:/app/src:rw + - ./bci/ui/frontend/postcss.config.js:/app/postcss.config.js:rw + - ./bci/ui/frontend/tailwind.config.js:/app/tailwind.config.js:rw + - ./bci/ui/frontend/vite.config.js:/app/vite.config.js:rw + command: ["npm", "run", "dev", "--", "--host"] profiles: - dev From 127669c5d32f31612422ef62d3bc499100679b8d Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Thu, 21 Mar 2024 11:15:38 +0000 Subject: [PATCH 03/13] Improve reproducibility reporting and small fixes. - Reproducibility reporting improvements - Docker compose improvements - Attempt at log collectiony --- analysis/plot_factory.py | 13 +++-- bci/browser/automation/terminal.py | 14 ++--- bci/browser/configuration/chromium.py | 12 +++-- bci/database/mongo/mongodb.py | 18 ++++--- bci/evaluations/collector.py | 48 ++++++++++++++++++ bci/evaluations/collectors/base.py | 42 +++++++++++++++ bci/evaluations/collectors/logs.py | 21 ++++++++ .../collectors/requests.py} | 17 +++++-- bci/evaluations/custom/custom_evaluation.py | 24 +++++---- bci/evaluations/evaluation_framework.py | 2 +- bci/evaluations/logic.py | 13 +++-- bci/evaluations/outcome_checker.py | 10 ++-- bci/ui/app.py | 6 +++ bci/worker.py | 3 ++ docker-compose.yml | 10 ++-- experiments/{resources => res}/600x400.png | Bin experiments/{resources => res}/CSP/c756018.as | 0 .../{resources => res}/CSP/c756018.swf | Bin experiments/res/bughog.js | 30 +++++++++++ test/http_collector/test_collector.py | 2 +- 20 files changed, 234 insertions(+), 51 deletions(-) create mode 100644 bci/evaluations/collector.py create mode 100644 bci/evaluations/collectors/base.py create mode 100644 bci/evaluations/collectors/logs.py rename bci/{http/collector.py => evaluations/collectors/requests.py} (77%) rename experiments/{resources => res}/600x400.png (100%) rename experiments/{resources => res}/CSP/c756018.as (100%) rename experiments/{resources => res}/CSP/c756018.swf (100%) create mode 100644 experiments/res/bughog.js diff --git a/analysis/plot_factory.py b/analysis/plot_factory.py index dfb4e85..504f96b 100644 --- a/analysis/plot_factory.py +++ b/analysis/plot_factory.py @@ -105,17 +105,24 @@ def __add_outcome_info(params: PlotParameters, docs: dict): target_mech_id = params.target_mech_id if params.target_mech_id else params.mech_group for doc in docs: + # Backwards compatibility requests_to_target = list(filter(lambda x: f'/report/?leak={target_mech_id}' in x['url'], doc['results']['requests'])) - requests_to_baseline = list(filter(lambda x: '/report/?leak=baseline' in x['url'], doc['results']['requests'])) + # New way + if [req_var for req_var in doc['results']['req_vars'] if req_var['var'] == 'reproduced' and req_var['val'] == 'OK'] or \ + [log_var for log_var in doc['results']['log_vars'] if log_var['var'] == 'reproduced' and log_var['val'] == 'OK']: + reproduced = True + else: + reproduced = False + new_doc = { 'revision_number': doc['revision_number'], 'browser_version': int(doc['browser_version'].split('.')[0]), 'browser_version_str': doc['browser_version'].split('.')[0] } - if doc['dirty'] or len(requests_to_baseline) == 0: + if doc['dirty']: new_doc['outcome'] = 'Error' docs_with_outcome.append(new_doc) - elif len(requests_to_target) > 0: + elif len(requests_to_target) > 0 or reproduced: new_doc['outcome'] = 'Reproduced' docs_with_outcome.append(new_doc) else: diff --git a/bci/browser/automation/terminal.py b/bci/browser/automation/terminal.py index d4b57b6..d1fe6f7 100644 --- a/bci/browser/automation/terminal.py +++ b/bci/browser/automation/terminal.py @@ -3,8 +3,7 @@ import subprocess import time - -logger = logging.getLogger('bci') +logger = logging.getLogger(__name__) class TerminalAutomation: @@ -14,11 +13,12 @@ def run(url: str, args: list[str], seconds_per_visit: int): logger.debug("Starting browser process...") args.append(url) logger.debug(f'Command string: \'{" ".join(args)}\'') - proc = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + with open('/tmp/browser.log', 'a') as file: + proc = subprocess.Popen( + args, + stdout=file, + stderr=file + ) time.sleep(seconds_per_visit) diff --git a/bci/browser/configuration/chromium.py b/bci/browser/configuration/chromium.py index 4bc95ed..6108328 100644 --- a/bci/browser/configuration/chromium.py +++ b/bci/browser/configuration/chromium.py @@ -11,7 +11,6 @@ SELENIUM_USED_FLAGS = [ '--use-fake-ui-for-media-stream', '--ignore-certificate-errors', - '--use-fake-ui-for-media-stream', '--disable-background-networking', '--disable-client-side-phishing-detection', '--disable-component-update', @@ -22,15 +21,12 @@ '--disable-prompt-on-repost', '--disable-sync', '--disable-web-resources', - '--enable-logging', - '--log-level=0', '--metrics-recording-only', '--no-first-run', '--password-store=basic', '--safebrowsing-disable-auto-update', '--use-mock-keychain', '--no-sandbox', - '--ignore-certificate-errors' ] @@ -41,6 +37,14 @@ def _get_terminal_args(self) -> list[str]: args = [self._get_executable_file_path()] args.append(f'--user-data-dir={self._profile_path}') + # Enable logging + args.append('--enable-logging') + args.append('--v=1') + args.append('--log-level=0') + # Headless changed from version +/- 110 onwards: https://developer.chrome.com/docs/chromium/new-headless + # Using the `--headless` flag will crash the browser for these later versions. + # Also see: https://github.com/DistriNet/BugHog/issues/12 + # args.append('--headless=new') # From Chrome if 'btpc' in self.browser_config.browser_setting: # This is handled in the profile folder diff --git a/bci/database/mongo/mongodb.py b/bci/database/mongo/mongodb.py index b560970..36c12d9 100644 --- a/bci/database/mongo/mongodb.py +++ b/bci/database/mongo/mongodb.py @@ -98,7 +98,7 @@ def store_result(self, result: TestResult): 'revision_id': result.params.state.revision_id, 'revision_number': result.params.state.revision_number, 'mech_group': result.params.mech_group, - 'results': result.requests, + 'results': result.data, 'dirty': result.is_dirty, 'ts': str(datetime.now(timezone.utc).replace(microsecond=0)) } @@ -119,13 +119,15 @@ def get_result(self, params: TestParameters) -> TestResult: collection = self.__get_data_collection(params) query = self.__to_query(params) document = collection.find_one(query) - return TestResult( - params, - document['browser_version'], - document['binary_origin'], - requests=document['results']['requests'] if 'requests' in document['results'] else None, - is_dirty=document['dirty'] - ) + if document: + return params.create_test_result_with( + document['browser_version'], + document['binary_origin'], + document['results'], + document['dirty'] + ) + else: + logger.error(f'Could not find document for query {query}') def has_result(self, params: TestParameters) -> bool: collection = self.__get_data_collection(params) diff --git a/bci/evaluations/collector.py b/bci/evaluations/collector.py new file mode 100644 index 0000000..65b8dd8 --- /dev/null +++ b/bci/evaluations/collector.py @@ -0,0 +1,48 @@ +from abc import abstractmethod +from enum import Enum +import logging + +from bci.evaluations.collectors.base import BaseCollector + +from .collectors.requests import RequestCollector +from .collectors.logs import LogCollector + +logger = logging.getLogger(__name__) + + +class Type(Enum): + REQUESTS = 1 + LOGS = 2 + + +class Collector: + + def __init__(self, types: list[Type]) -> None: + self.collectors: list[BaseCollector] = [] + if Type.REQUESTS in types: + collector = RequestCollector() + self.collectors.append(collector) + if Type.LOGS in types: + collector = LogCollector() + self.collectors.append(collector) + logger.debug(f'Using {len(self.collectors)} result collectors') + + def start(self): + for collector in self.collectors: + collector.start() + + def stop(self): + for collector in self.collectors: + collector.stop() + + @abstractmethod + def collect_results(self) -> dict: + all_data = {} + for collector in self.collectors: + all_data.update(collector.data) + logger.debug(f'Collected data: {all_data}') + return all_data + + + + diff --git a/bci/evaluations/collectors/base.py b/bci/evaluations/collectors/base.py new file mode 100644 index 0000000..6591d92 --- /dev/null +++ b/bci/evaluations/collectors/base.py @@ -0,0 +1,42 @@ +import re +from abc import abstractmethod + + +class BaseCollector: + + def __init__(self) -> None: + self.data = {} + + @abstractmethod + def start(): + pass + + @abstractmethod + def stop(): + pass + + @staticmethod + def _parse_bughog_variables(raw_log_lines: list[str], regex) -> list[tuple[str, str]]: + ''' + Parses the given `raw_log_lines` for matches against the given `regex`. + ''' + data = [] + regex_match_lists = [re.findall(regex, line) for line in raw_log_lines if re.search(regex, line)] + # Flatten list + regex_matches = [regex_match for regex_match_list in regex_match_lists for regex_match in regex_match_list] + for match in regex_matches: + var = match[0] + val = match[1] + BaseCollector._add_val_var_pair(var, val, data) + return data + + + @staticmethod + def _add_val_var_pair(var: str, val: str, data: list) -> list: + for entry in data: + if entry['var'] == var and entry['val'] == val: + return data + data.append({ + 'var': var, + 'val': val + }) diff --git a/bci/evaluations/collectors/logs.py b/bci/evaluations/collectors/logs.py new file mode 100644 index 0000000..48a2078 --- /dev/null +++ b/bci/evaluations/collectors/logs.py @@ -0,0 +1,21 @@ +from .base import BaseCollector + + +class LogCollector(BaseCollector): + + def __init__(self) -> None: + super().__init__() + self.data['log_vars'] = [] + + def start(self): + with open('/tmp/browser.log', 'w') as file: + file.write('') + + def stop(self): + data = [] + regex = r'\+\+\+bughog_(.+)=(.+)\+\+\+' + with open('/tmp/browser.log', 'r+') as log_file: + log_lines = [line for line in log_file.readlines()] + log_file.write('') + data = self._parse_bughog_variables(log_lines, regex) + self.data['log_vars'] = data diff --git a/bci/http/collector.py b/bci/evaluations/collectors/requests.py similarity index 77% rename from bci/http/collector.py rename to bci/evaluations/collectors/requests.py index 759afc1..6305505 100644 --- a/bci/http/collector.py +++ b/bci/evaluations/collectors/requests.py @@ -1,10 +1,11 @@ - import http.server import json import logging import socketserver from threading import Thread +from .base import BaseCollector + logger = logging.getLogger(__name__) PORT = 5001 @@ -24,7 +25,7 @@ def log_message(self, *_): logger.debug(f'Received request with body: {self.request_body}') request_body = json.loads(self.request_body) - self.collector.requests.append(request_body) + self.collector.data['requests'].append(request_body) def do_POST(self): content_length = int(self.headers['Content-Length']) @@ -35,12 +36,14 @@ def do_POST(self): self.wfile.write(b'Post request received') -class Collector: +class RequestCollector(BaseCollector): def __init__(self): + super().__init__() self.__httpd = None self.__thread = None - self.requests = [] + self.data['requests'] = [] + self.data['req_vars'] = [] def start(self): logger.debug('Starting collector...') @@ -51,8 +54,12 @@ def start(self): self.__thread.start() def stop(self): - logger.debug('Stopping collector...') + data = [] + regex = r'bughog_(.+)=(.+)' if self.__httpd: self.__httpd.shutdown() self.__thread.join() self.__httpd.server_close() + request_urls = [request['url'] for request in self.data['requests']] + data = self._parse_bughog_variables(request_urls, regex) + self.data['req_vars'] = data diff --git a/bci/evaluations/custom/custom_evaluation.py b/bci/evaluations/custom/custom_evaluation.py index cc60f7d..ff2e387 100644 --- a/bci/evaluations/custom/custom_evaluation.py +++ b/bci/evaluations/custom/custom_evaluation.py @@ -1,14 +1,14 @@ import logging import os from unittest import TestResult -from bci.browser.configuration.browser import Browser +from bci.browser.configuration.browser import Browser from bci.configuration import Global +from bci.evaluations.collector import Collector +from bci.evaluations.collector import Type from bci.evaluations.custom.custom_mongodb import CustomMongoDB from bci.evaluations.evaluation_framework import EvaluationFramework from bci.evaluations.logic import TestParameters -from bci.http.collector import Collector - logger = logging.getLogger(__name__) @@ -45,7 +45,7 @@ def initialize_tests_and_url_queues(self): if os.path.exists(main_folder_path): self.tests_per_project[project_name][test_name] = [ f'https://{domain}/{project_name}/{test_name}/main', - 'https://a.test/report/?leak=baseline' + 'https://a.test/report/?bughog_sanity_check=OK' ] self.tests[test_name] = self.tests_per_project[project_name][test_name] @@ -54,7 +54,7 @@ def perform_specific_evaluation(self, browser: Browser, params: TestParameters) browser_version = browser.version binary_origin = browser.get_binary_origin() - collector = Collector() + collector = Collector([Type.REQUESTS, Type.LOGS]) collector.start() is_dirty = False @@ -70,13 +70,17 @@ def perform_specific_evaluation(self, browser: Browser, params: TestParameters) is_dirty = True finally: collector.stop() + data = collector.collect_results() if not is_dirty: - if len([request for request in collector.requests if 'report/?leak=baseline' in request['url']]) == 0: + # New way to perform sanity check + if [var_entry for var_entry in data['req_vars'] if var_entry['var'] == 'sanity_check' and var_entry['val'] == 'OK']: + pass + # Old way for backwards compatibility + elif [request for request in data['requests'] if 'report/?leak=baseline' in request['url']]: + pass + else: is_dirty = True - result = { - 'requests': collector.requests - } - return params.create_test_result_with(browser_version, binary_origin, result, is_dirty) + return params.create_test_result_with(browser_version, binary_origin, data, is_dirty) def get_mech_groups(self, project=None): if project: diff --git a/bci/evaluations/evaluation_framework.py b/bci/evaluations/evaluation_framework.py index e31c767..fe09276 100644 --- a/bci/evaluations/evaluation_framework.py +++ b/bci/evaluations/evaluation_framework.py @@ -8,7 +8,7 @@ from bci.database.mongo.mongodb import MongoDB from bci.evaluations.logic import TestParameters, TestResult, WorkerParameters -logger = logging.getLogger('bci') +logger = logging.getLogger(__name__) class EvaluationFramework(ABC): diff --git a/bci/evaluations/logic.py b/bci/evaluations/logic.py index dfb1358..29d9f88 100644 --- a/bci/evaluations/logic.py +++ b/bci/evaluations/logic.py @@ -223,12 +223,12 @@ class TestParameters: mech_group: str database_collection: str - def create_test_result_with(self, browser_version: str, binary_origin: str, result: dict, dirty: bool) -> TestResult: + def create_test_result_with(self, browser_version: str, binary_origin: str, data: dict, dirty: bool) -> TestResult: return TestResult( self, browser_version, binary_origin, - result, + data, dirty ) @@ -238,7 +238,7 @@ class TestResult: params: TestParameters browser_version: str binary_origin: str - requests: list | None = None + data: dict is_dirty: bool = False driver_version: str | None = None @@ -252,6 +252,13 @@ def padded_browser_version(self): padded_version.append('0' * (padding_target - len(sub)) + sub) return ".".join(padded_version) + @property + def reproduced(self): + entry_if_reproduced = {'val': 'reproduced', 'var': 'OK'} + reproduced_in_req_vars = [entry for entry in self.data['req_vars'] if entry == entry_if_reproduced] != [] + reproduced_in_log_vars = [entry for entry in self.data['log_vars'] if entry == entry_if_reproduced] != [] + return reproduced_in_req_vars or reproduced_in_log_vars + @dataclass(frozen=True) class PlotParameters: diff --git a/bci/evaluations/outcome_checker.py b/bci/evaluations/outcome_checker.py index 1c3fa14..fd5d4c2 100644 --- a/bci/evaluations/outcome_checker.py +++ b/bci/evaluations/outcome_checker.py @@ -11,16 +11,20 @@ def __init__(self, sequence_config: SequenceConfiguration): @abstractmethod def get_outcome(self, result: TestResult) -> bool: + if result.reproduced: + return True + # Backwards compatibility if self.sequence_config.target_mech_id: - return self.get_outcome_for_proxy(result) + return (outcome := self.get_outcome_for_proxy(result)) def get_outcome_for_proxy(self, result: TestResult) -> bool | None: target_mech_id = self.sequence_config.target_mech_id target_cookie = self.sequence_config.target_cookie_name - if result.requests is None: + requests = result.data.get('requests') + if requests is None: return None regex = rf'^https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z]+\/report\/\?leak={target_mech_id}$' - requests_to_result_endpoint = [request for request in result.requests if re.match(regex, request['url'])] + requests_to_result_endpoint = [request for request in requests if re.match(regex, request['url'])] for request in requests_to_result_endpoint: headers = request['headers'] if not target_cookie: diff --git a/bci/ui/app.py b/bci/ui/app.py index 11a2d01..25a92c7 100644 --- a/bci/ui/app.py +++ b/bci/ui/app.py @@ -26,3 +26,9 @@ def index(): def serve_static_files(file_path): path = os.path.join('dist', file_path) return send_from_directory('frontend', path) + + +if __name__ == '__main__': + # Used when running in devcontainer + app = create_app() + app.run(debug=False, host='0.0.0.0') diff --git a/bci/worker.py b/bci/worker.py index dfb5c27..2f1a44c 100644 --- a/bci/worker.py +++ b/bci/worker.py @@ -39,6 +39,9 @@ def get_evaluation_framework(params: WorkerParameters): if __name__ == '__main__': + if len(sys.argv) < 2: + logger.info('Worker did not receive any arguments.') + os._exit(0) args = sys.argv[1] params = WorkerParameters.deserialize(args) run(params) diff --git a/docker-compose.yml b/docker-compose.yml index 75e482c..fd1844e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,8 +22,6 @@ services: required: false environment: - HOST_PWD=${PWD} - ports: - - "5000:5000" volumes: - ./config:/app/config:ro - ./browser/binaries/chromium/artisanal:/app/browser/binaries/chromium/artisanal:rw @@ -68,6 +66,8 @@ services: image: bughog/core:${BUGHOG_VERSION} hostname: bh_core container_name: bh_core + ports: + - "5000:5000" profiles: - prod @@ -77,11 +77,10 @@ services: build: context: . target: worker - pull_policy: if_not_present hostname: bh_worker container_name: bh_worker profiles: - - never_start + - prod #===================# # EXPERIMENT SERVER # @@ -89,10 +88,9 @@ services: web: image: bughog/web:${BUGHOG_WEB_VERSION} - pull_policy: if_not_present volumes: - ./experiments/pages:/experiments/pages:ro - - ./experiments/resources:/app/static/resources:ro + - ./experiments/res:/app/static/res:ro container_name: bh_web ports: - "80:80" diff --git a/experiments/resources/600x400.png b/experiments/res/600x400.png similarity index 100% rename from experiments/resources/600x400.png rename to experiments/res/600x400.png diff --git a/experiments/resources/CSP/c756018.as b/experiments/res/CSP/c756018.as similarity index 100% rename from experiments/resources/CSP/c756018.as rename to experiments/res/CSP/c756018.as diff --git a/experiments/resources/CSP/c756018.swf b/experiments/res/CSP/c756018.swf similarity index 100% rename from experiments/resources/CSP/c756018.swf rename to experiments/res/CSP/c756018.swf diff --git a/experiments/res/bughog.js b/experiments/res/bughog.js new file mode 100644 index 0000000..c7f5970 --- /dev/null +++ b/experiments/res/bughog.js @@ -0,0 +1,30 @@ +"use strict"; + +function report_only_log(variable, value) { + console.log("+++bughog_" + variable + "=" + value + "+++"); +} + +function report_only_request(variable, value, domain) { + var url = ""; + var path = "/report/?bughog_" + variable + "=" + value; + if (domain) { + url = "//" + domain + path; + } else { + url = path; + } + var img = document.createElement('img'); + img.src = url; + document.body.appendChild(img); +} + +function report(variable, value, domain) { + report_only_log(variable, value); + report_only_request(variable, value, domain); +} + +function reproduced(domain) { + var variable = 'reproduced'; + var value = 'OK'; + report_only_log(variable, value); + report_only_request(variable, value, domain); +} diff --git a/test/http_collector/test_collector.py b/test/http_collector/test_collector.py index ffabcb8..06dca9f 100644 --- a/test/http_collector/test_collector.py +++ b/test/http_collector/test_collector.py @@ -3,7 +3,7 @@ import requests -from bci.http.collector import Collector +from bci.evaluations.result_collector.collector import Collector class TestCollector(unittest.TestCase): From 7ac152c3b603e3636bd2bf440b7b25e98512545e Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Thu, 21 Mar 2024 12:22:10 +0100 Subject: [PATCH 04/13] Bump dependencies --- requirements.txt | 14 +++++++------- requirements_dev.txt | 31 +++++++++++++++---------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/requirements.txt b/requirements.txt index 670891e..ba9797c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ attrs==23.2.0 # via service-identity blinker==1.7.0 # via flask -bokeh==3.3.4 +bokeh==3.4.0 # via -r requirements.in brotli==1.1.0 # via mitmproxy @@ -29,7 +29,7 @@ click==8.1.7 # via flask contourpy==1.2.0 # via bokeh -cryptography==41.0.7 +cryptography==42.0.5 # via # aioquic # mitmproxy @@ -73,7 +73,7 @@ markupsafe==2.1.5 # via # jinja2 # werkzeug -mitmproxy==10.2.2 +mitmproxy==10.2.4 # via -r requirements.in mitmproxy-macos==0.5.1 # via mitmproxy-rs @@ -86,7 +86,7 @@ numpy==1.26.4 # bokeh # contourpy # pandas -packaging==23.2 +packaging==24.0 # via # bokeh # docker @@ -114,15 +114,15 @@ pylsqpack==0.3.18 # via aioquic pymongo==4.6.2 # via -r requirements.in -pyopenssl==23.3.0 +pyopenssl==24.0.0 # via # aioquic # mitmproxy -pyparsing==3.1.1 +pyparsing==3.1.2 # via mitmproxy pyperclip==1.8.2 # via mitmproxy -python-dateutil==2.9.0 +python-dateutil==2.9.0.post0 # via pandas pytz==2024.1 # via pandas diff --git a/requirements_dev.txt b/requirements_dev.txt index a5f2b8b..5a62007 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -20,17 +20,17 @@ attrs==23.2.0 # via # -r requirements.txt # service-identity -autopep8==2.0.4 +autopep8==2.1.0 # via -r requirements_dev.in blinker==1.7.0 # via # -r requirements.txt # flask -bokeh==3.3.4 +bokeh==3.4.0 # via -r requirements.txt -boto3==1.7.84 +boto3==1.34.67 # via -r requirements_dev.in -botocore==1.10.84 +botocore==1.34.67 # via # -r requirements_dev.in # boto3 @@ -61,11 +61,11 @@ contourpy==1.2.0 # via # -r requirements.txt # bokeh -coverage[toml]==7.4.3 +coverage[toml]==7.4.4 # via # -r requirements_dev.in # pytest-cov -cryptography==41.0.7 +cryptography==42.0.5 # via # -r requirements.txt # aioquic @@ -82,8 +82,6 @@ dnspython==2.6.1 # pymongo docker==7.0.0 # via -r requirements.txt -docutils==0.20.1 - # via botocore flake8==7.0.0 # via # -r requirements_dev.in @@ -131,7 +129,7 @@ jinja2==3.1.3 # -r requirements.txt # bokeh # flask -jmespath==0.10.0 +jmespath==1.0.1 # via # boto3 # botocore @@ -152,7 +150,7 @@ mccabe==0.7.0 # via # flake8 # pylint -mitmproxy==10.2.2 +mitmproxy==10.2.4 # via -r requirements.txt mitmproxy-macos==0.5.1 # via @@ -172,7 +170,7 @@ numpy==1.26.4 # bokeh # contourpy # pandas -packaging==23.2 +packaging==24.0 # via # -r requirements.txt # anybadge @@ -232,12 +230,12 @@ pylsqpack==0.3.18 # aioquic pymongo==4.6.2 # via -r requirements.txt -pyopenssl==23.3.0 +pyopenssl==24.0.0 # via # -r requirements.txt # aioquic # mitmproxy -pyparsing==3.1.1 +pyparsing==3.1.2 # via # -r requirements.txt # mitmproxy @@ -245,7 +243,7 @@ pyperclip==1.8.2 # via # -r requirements.txt # mitmproxy -pytest==8.0.2 +pytest==8.1.1 # via # -r requirements_dev.in # pytest-cov @@ -254,7 +252,7 @@ pytest-cov==4.1.0 # via -r requirements_dev.in pytest-flake8==1.1.1 # via -r requirements_dev.in -python-dateutil==2.9.0 +python-dateutil==2.9.0.post0 # via # -r requirements.txt # botocore @@ -279,7 +277,7 @@ ruamel-yaml-clib==0.2.8 # via # -r requirements.txt # ruamel-yaml -s3transfer==0.1.13 +s3transfer==0.10.1 # via boto3 service-identity==24.1.0 # via @@ -307,6 +305,7 @@ tzdata==2024.1 urllib3==2.2.1 # via # -r requirements.txt + # botocore # docker # requests urwid-mitmproxy==2.1.2.1 From ac724f282127d5e55f855d68c4f32cf30ab7e7e3 Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Thu, 21 Mar 2024 11:35:59 +0000 Subject: [PATCH 05/13] Fix bokeh bump issue --- analysis/plot_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis/plot_factory.py b/analysis/plot_factory.py index 504f96b..9ca3eb6 100644 --- a/analysis/plot_factory.py +++ b/analysis/plot_factory.py @@ -75,7 +75,7 @@ def __create_plot(params: PlotParameters, db: MongoDB): width=700, tools='xwheel_zoom,reset,pan', active_scroll='xwheel_zoom') - plot.add_glyph(source, Circle(x='revision_number', y='outcome', fill_color=color_map, fill_alpha=0.8, size=15)) + plot.add_glyph(source, Circle(x='revision_number', y='outcome', fill_color=color_map, fill_alpha=0.8, radius=8, radius_units='screen')) # Formatting plot.xaxis[0].formatter = BasicTickFormatter(use_scientific=False) From 70fd8d1092d85fcf87ca9db5cae7e8aea5f19ad4 Mon Sep 17 00:00:00 2001 From: Gertjan Franken Date: Thu, 21 Mar 2024 16:26:12 +0000 Subject: [PATCH 06/13] Change browser version range input to slider --- bci/browser/support.py | 23 ++++ bci/main.py | 27 +--- bci/ui/blueprints/api.py | 32 +---- bci/ui/frontend/package-lock.json | 6 + bci/ui/frontend/package.json | 1 + bci/ui/frontend/src/App.vue | 84 ++++++------ bci/ui/frontend/src/style.css | 7 + .../repository/local/__init__.py | 0 .../repository/local/chromium.py | 31 ----- .../repository/local/firefox.py | 124 ------------------ .../repository/online/chromium.py | 42 ++---- .../repository/online/firefox.py | 37 +++--- .../repository/online/parser.py | 20 +++ .../revision_parser/chromium_parser.py | 2 +- .../revision_parser/parser.py | 0 bci/version_control/state_factory.py | 10 +- bci/version_control/states/chromium.py | 2 +- 17 files changed, 142 insertions(+), 306 deletions(-) create mode 100644 bci/browser/support.py delete mode 100644 bci/version_control/repository/local/__init__.py delete mode 100644 bci/version_control/repository/local/chromium.py delete mode 100644 bci/version_control/repository/local/firefox.py create mode 100644 bci/version_control/repository/online/parser.py rename bci/{ => version_control}/revision_parser/chromium_parser.py (95%) rename bci/{ => version_control}/revision_parser/parser.py (100%) diff --git a/bci/browser/support.py b/bci/browser/support.py new file mode 100644 index 0000000..a122b26 --- /dev/null +++ b/bci/browser/support.py @@ -0,0 +1,23 @@ +import dataclasses + +import bci.version_control.repository.online.chromium as chromium_repo +import bci.version_control.repository.online.firefox as firefox_repo + +from bci.browser.configuration import chromium, firefox + + +def get_chromium_support() -> dict: + return { + 'name': 'chromium', + 'min_version': 20, + 'max_version': chromium_repo.get_most_recent_major_version(), + 'options': [dataclasses.asdict(option) for option in chromium.SUPPORTED_OPTIONS] + } + +def get_firefox_support() -> dict: + return { + 'name': 'firefox', + 'min_version': 20, + 'max_version': firefox_repo.get_most_recent_major_version(), + 'options': [dataclasses.asdict(option) for option in firefox.SUPPORTED_OPTIONS] + } diff --git a/bci/main.py b/bci/main.py index e49bfee..6584345 100644 --- a/bci/main.py +++ b/bci/main.py @@ -1,9 +1,8 @@ -import dataclasses import logging import os import bci.browser.binary.factory as binary_factory -from bci.browser.configuration import chromium, firefox +from bci.browser.support import get_chromium_support, get_firefox_support from bci.configuration import Global, Loggers from bci.database.mongo.mongodb import MongoDB from bci.evaluations.logic import EvaluationParameters, PlotParameters @@ -61,30 +60,12 @@ def get_database_info() -> dict: return MongoDB.get_info() @staticmethod - def get_browsers() -> list[str]: + def get_browser_support() -> list[dict]: return [ - 'chromium', - 'firefox' + get_chromium_support(), + get_firefox_support() ] - @staticmethod - def get_browser_options(browser_name: str) -> list[dict[str, str]]: - match browser_name: - case 'chromium': - return [dataclasses.asdict(option) for option in chromium.SUPPORTED_OPTIONS] - case 'firefox': - return [dataclasses.asdict(option) for option in firefox.SUPPORTED_OPTIONS] - case _: - raise AttributeError(f'Browser \'{browser_name}\' is not supported') - - @staticmethod - def get_available_extensions(browser: str) -> list[str]: - extensions = [] - folder_path = Global.get_extension_folder(browser) - for _, _, files in os.walk(folder_path): - extensions.extend(files) - return list(filter(lambda x: x != '.gitkeep', extensions)) - @staticmethod def list_downloaded_binaries(browser): return binary_factory.list_downloaded_binaries(browser) diff --git a/bci/ui/blueprints/api.py b/bci/ui/blueprints/api.py index 5a06671..46ff813 100644 --- a/bci/ui/blueprints/api.py +++ b/bci/ui/blueprints/api.py @@ -113,7 +113,7 @@ def get_info(): def get_browsers(): return { 'status': 'OK', - 'browsers': bci_api.get_browsers() + 'browsers': bci_api.get_browser_support() } @@ -125,36 +125,6 @@ def get_projects(): } -@api.route('/options//', methods=['GET']) -def get_options(browser_name: str): - try: - options = bci_api.get_browser_options(browser_name) - return { - 'status': 'OK', - 'options': options - } - except Exception as e: - return { - 'status': 'NOK', - 'msg': str(e) - } - - -@api.route('/extensions//', methods=['GET']) -def get_extensions(browser_name: str): - try: - extensions = bci_api.get_available_extensions(browser_name) - return { - 'status': 'OK', - 'extensions': extensions - } - except Exception as e: - return { - 'status': 'NOK', - 'msg': str(e) - } - - @api.route('/tests//', methods=['GET']) def get_tests(project: str): tests = bci_api.get_mech_groups_of_evaluation_framework('custom', project=project) diff --git a/bci/ui/frontend/package-lock.json b/bci/ui/frontend/package-lock.json index d7b51d4..1cf4b37 100644 --- a/bci/ui/frontend/package-lock.json +++ b/bci/ui/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "app", "version": "0.0.0", "dependencies": { + "@vueform/slider": "^2.1.10", "axios": "^1.4.0", "flowbite": "^1.6.5", "oh-vue-icons": "^1.0.0-rc3", @@ -618,6 +619,11 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz", "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==" }, + "node_modules/@vueform/slider": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@vueform/slider/-/slider-2.1.10.tgz", + "integrity": "sha512-L2G3Ju51Yq6yWF2wzYYsicUUaH56kL1QKGVtimUVHT1K1ADcRT94xVyIeJpS0klliVEeF6iMZFbdXtHq8AsDHw==" + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", diff --git a/bci/ui/frontend/package.json b/bci/ui/frontend/package.json index bba8903..d74f717 100644 --- a/bci/ui/frontend/package.json +++ b/bci/ui/frontend/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@vueform/slider": "^2.1.10", "axios": "^1.4.0", "flowbite": "^1.6.5", "oh-vue-icons": "^1.0.0-rc3", diff --git a/bci/ui/frontend/src/App.vue b/bci/ui/frontend/src/App.vue index 403f7e0..c8f20a4 100644 --- a/bci/ui/frontend/src/App.vue +++ b/bci/ui/frontend/src/App.vue @@ -1,22 +1,25 @@ +