├── tests ├── __init__.py └── test_braveblock.py ├── images └── logo.png ├── braveblock ├── easylist.txt.gz ├── easyprivacy.txt.gz ├── braveblock.pyi └── __init__.py ├── cortex.yaml ├── scripts └── update_lists.py ├── Cargo.toml ├── LICENSE ├── pyproject.toml ├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── README.md ├── .gitignore ├── src └── lib.rs └── poetry.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intsights/braveblock/HEAD/images/logo.png -------------------------------------------------------------------------------- /braveblock/easylist.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intsights/braveblock/HEAD/braveblock/easylist.txt.gz -------------------------------------------------------------------------------- /braveblock/easyprivacy.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Intsights/braveblock/HEAD/braveblock/easyprivacy.txt.gz -------------------------------------------------------------------------------- /braveblock/braveblock.pyi: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | 4 | class Adblocker: 5 | def __init__( 6 | self, 7 | rules: typing.Optional[typing.List[str]] = None, 8 | include_easylist: bool = True, 9 | include_easyprivacy: bool = True, 10 | ) -> None: ... 11 | 12 | def check_network_urls( 13 | self, 14 | url: str, 15 | source_url: str, 16 | request_type: str, 17 | ) -> bool: ... 18 | -------------------------------------------------------------------------------- /cortex.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | info: 3 | title: Braveblock 4 | description: A fast and easy adblockplus parser and matcher based on adblock-rust 5 | package 6 | x-cortex-git: 7 | github: 8 | alias: intsightsorg 9 | repository: Intsights/braveblock 10 | x-cortex-tag: braveblock 11 | x-cortex-type: service 12 | x-cortex-domain-parents: 13 | - tag: threatintel-brand-security 14 | x-cortex-groups: 15 | - exposure:internal-ship 16 | - target:library 17 | openapi: 3.0.1 18 | servers: 19 | - url: "/" 20 | -------------------------------------------------------------------------------- /scripts/update_lists.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import gzip 3 | import pathlib 4 | 5 | 6 | braveblock_dir = pathlib.Path(__file__).parent.parent.joinpath('braveblock') 7 | 8 | easylist_response = requests.get( 9 | url='https://easylist.to/easylist/easylist.txt', 10 | ) 11 | with gzip.GzipFile( 12 | filename=braveblock_dir.joinpath('easylist.txt.gz'), 13 | mode='wb', 14 | ) as easylist_file: 15 | easylist_file.write(easylist_response.content) 16 | 17 | easyprivacy_response = requests.get( 18 | url='https://easylist.to/easylist/easyprivacy.txt', 19 | ) 20 | with gzip.GzipFile( 21 | filename=braveblock_dir.joinpath('easyprivacy.txt.gz'), 22 | mode='wb', 23 | ) as easyprivacy_file: 24 | easyprivacy_file.write(easyprivacy_response.content) 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "braveblock" 3 | version = "0.5.1" 4 | authors = ["Gal Ben David "] 5 | edition = "2021" 6 | description = "A fast and easy adblockplus parser and matcher based on adblock-rust package" 7 | readme = "README.md" 8 | repository = "https://github.com/intsights/braveblock" 9 | homepage = "https://github.com/intsights/braveblock" 10 | license = "MIT" 11 | keywords = [ 12 | "adblock", 13 | "ads", 14 | "adblocker", 15 | "rust", 16 | "brave", 17 | "abp", 18 | "pyo3", 19 | ] 20 | 21 | [package.metadata.maturin] 22 | 23 | [lib] 24 | name = "braveblock" 25 | crate-type = ["cdylib"] 26 | 27 | [dependencies.adblock] 28 | version = "0.5" 29 | default-features = false 30 | features = ["full-regex-handling", "embedded-domain-resolver"] 31 | 32 | [dependencies.pyo3] 33 | version = "0.16.5" 34 | features = ["extension-module"] 35 | 36 | [profile.release] 37 | lto = true 38 | panic = "abort" 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gal Ben David 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=0.12,<0.13"] 3 | build-backend = "maturin" 4 | 5 | [tool.maturin] 6 | sdist-include = [ 7 | "braveblock/*.py", 8 | "braveblock/*.pyi", 9 | "Cargo.toml", 10 | "pyproject.toml", 11 | "src/*", 12 | ] 13 | 14 | [tool.poetry] 15 | name = "braveblock" 16 | version = "0.5.1" 17 | authors = ["Gal Ben David "] 18 | description = "A fast and easy adblockplus parser and matcher based on adblock-rust package" 19 | readme = "README.md" 20 | repository = "https://github.com/intsights/braveblock" 21 | homepage = "https://github.com/intsights/braveblock" 22 | license = "MIT" 23 | keywords = [ 24 | "adblock", 25 | "ads", 26 | "adblocker", 27 | "rust", 28 | "brave", 29 | "abp", 30 | "pyo3" 31 | ] 32 | classifiers = [ 33 | "License :: OSI Approved :: MIT License", 34 | "Operating System :: MacOS", 35 | "Operating System :: Microsoft", 36 | "Operating System :: POSIX :: Linux", 37 | "Programming Language :: Python :: 3.7", 38 | "Programming Language :: Python :: 3.8", 39 | "Programming Language :: Python :: 3.9", 40 | "Programming Language :: Python :: 3.10", 41 | "Programming Language :: Python :: 3.11", 42 | "Programming Language :: Rust", 43 | ] 44 | 45 | [tool.poetry.dependencies] 46 | python = "^3.7" 47 | 48 | [tool.poetry.dev-dependencies] 49 | pytest = "*" 50 | wheel = "*" 51 | pytest-runner = "*" 52 | maturin = "*" 53 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | lint: 7 | if: github.event_name == 'push' && !startsWith(github.event.ref, 'refs/tags') 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | - name: Install latest rust 13 | uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | profile: minimal 17 | override: true 18 | components: clippy 19 | - name: Lint with clippy 20 | uses: actions-rs/cargo@v1 21 | with: 22 | command: clippy 23 | args: --all-targets --all-features 24 | test: 25 | runs-on: ${{ matrix.os }} 26 | needs: lint 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | python-version: 31 | - '3.7' 32 | - '3.8' 33 | - '3.9' 34 | - '3.10' 35 | - '3.11' 36 | os: 37 | - ubuntu-latest 38 | - macos-latest 39 | - windows-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | - name: Set up Python ${{ matrix.python-version }} 44 | uses: actions/setup-python@v3 45 | with: 46 | python-version: ${{ matrix.python-version }} 47 | - name: Install Poetry 48 | uses: abatilo/actions-poetry@v2.1.3 49 | - name: Install Rust 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: stable 54 | override: true 55 | - name: Install dependencies 56 | run: poetry install 57 | - name: Build Python package 58 | run: poetry run maturin develop 59 | - name: Test 60 | run: poetry run pytest -Werror tests 61 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | release: 4 | types: 5 | - released 6 | jobs: 7 | deploy: 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | python-version: 13 | - '3.7' 14 | - '3.8' 15 | - '3.9' 16 | - '3.10' 17 | - '3.11' 18 | os: 19 | - ubuntu-latest 20 | - macos-latest 21 | - windows-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v4 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install Rust 30 | uses: actions-rs/toolchain@v1 31 | with: 32 | profile: minimal 33 | toolchain: stable 34 | override: true 35 | - name: Install Cross-compilers (macOS) 36 | if: matrix.os == 'macos-latest' 37 | run: | 38 | rustup target add x86_64-apple-darwin 39 | rustup target add aarch64-apple-darwin 40 | - name: Publish Package 41 | uses: PyO3/maturin-action@v1 42 | with: 43 | command: publish 44 | args: --username=__token__ ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.7' && '' || '--no-sdist' }} --interpreter=python${{ !startsWith(matrix.os, 'windows') && matrix.python-version || '' }} 45 | env: 46 | MATURIN_PASSWORD: ${{ secrets.pypi_password }} 47 | if: matrix.os != 'macos-latest' 48 | - name: Publish macOS (x86_64) Package 49 | if: matrix.os == 'macos-latest' 50 | uses: PyO3/maturin-action@v1 51 | with: 52 | command: publish 53 | args: --username=__token__ --interpreter=python${{ matrix.python-version }} --target=x86_64-apple-darwin --no-sdist 54 | env: 55 | MATURIN_PASSWORD: ${{ secrets.pypi_password }} 56 | - name: Publish macOS (arm64) Package 57 | if: matrix.os == 'macos-latest' 58 | uses: PyO3/maturin-action@v1 59 | with: 60 | command: publish 61 | args: --username=__token__ --interpreter=python${{ matrix.python-version }} --target=aarch64-apple-darwin --no-sdist 62 | env: 63 | MATURIN_PASSWORD: ${{ secrets.pypi_password }} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Logo 4 | 5 |

6 | A fast and easy adblockplus parser and matcher based on adblock-rust package 7 |

8 |

9 | 10 | 11 | ![license](https://img.shields.io/badge/MIT-License-blue) 12 | ![Python](https://img.shields.io/badge/Python-3.7%20%7C%203.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-blue) 13 | ![OS](https://img.shields.io/badge/OS-Mac%20%7C%20Linux%20%7C%20Windows-blue) 14 | ![Build](https://github.com/intsights/Braveblock/workflows/Build/badge.svg) 15 | [![PyPi](https://img.shields.io/pypi/v/Braveblock.svg)](https://pypi.org/project/Braveblock/) 16 | 17 | ## Table of Contents 18 | 19 | - [Table of Contents](#table-of-contents) 20 | - [About The Project](#about-the-project) 21 | - [Built With](#built-with) 22 | - [Installation](#installation) 23 | - [Usage](#usage) 24 | - [License](#license) 25 | - [Contact](#contact) 26 | 27 | 28 | ## About The Project 29 | 30 | This library is a Python binding to the [adblock-rust](https://github.com/brave/adblock-rust) library that was written by Brave's browser team. The binding uses [pyo3](https://github.com/PyO3/pyo3) to interact with the rust package. 31 | 32 | 33 | ### Built With 34 | 35 | * [pyo3](https://github.com/PyO3/pyo3) 36 | * [adblock-rust](https://github.com/brave/adblock-rust) 37 | 38 | 39 | ### Installation 40 | 41 | ```sh 42 | pip3 install braveblock 43 | ``` 44 | 45 | 46 | ## Usage 47 | 48 | ```python 49 | import braveblock 50 | 51 | 52 | # Initialize the engine loaded with a rules list 53 | # One can download easylist and load its lines into the engine 54 | braveblock.Adblocker( 55 | rules=[ 56 | "-advertisement-icon.", 57 | "-advertisement/script.", 58 | ] 59 | ) 60 | 61 | # This function checks whether the specified url should be blocked 62 | adblocker.check_network_urls( 63 | url="http://example.com/-advertisement-icon.", 64 | source_url="http://example.com/helloworld", 65 | request_type="image", 66 | ) 67 | ``` 68 | 69 | 70 | ## License 71 | 72 | Distributed under the MIT License. See `LICENSE` for more information. 73 | 74 | 75 | ## Contact 76 | 77 | Gal Ben David - gal@intsights.com 78 | 79 | Project Link: [https://github.com/intsights/Braveblock](https://github.com/intsights/Braveblock) 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | # Distribution / packaging 8 | .Python 9 | env/ 10 | build/ 11 | develop-eggs/ 12 | dist/ 13 | downloads/ 14 | eggs/ 15 | .eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # conflict temp files 32 | *.py.orig 33 | *.mock 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | coverage_html_report/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | 58 | # Sphinx documentation 59 | docs/_build/ 60 | 61 | # PyBuilder 62 | target/ 63 | 64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 65 | 66 | *.iml 67 | 68 | ## Directory-based project format: 69 | .idea/ 70 | 71 | # Tests 72 | generic_tests.py 73 | cloudflare_test.py 74 | 75 | ############################ 76 | #Eclipse Specific GitIgnore# 77 | ############################ 78 | *.pydevproject 79 | .project 80 | .metadata 81 | bin/** 82 | tmp/** 83 | tmp/**/* 84 | *.tmp 85 | *.bak 86 | *.swp 87 | *~.nib 88 | local.properties 89 | .classpath 90 | .settings/ 91 | .loadpath 92 | 93 | 94 | # Git mergetool traces 95 | *.orig 96 | 97 | # VS Code internal directory 98 | .vscode/ 99 | 100 | *.dat 101 | *.code-workspace 102 | .history 103 | 104 | # Intsights development playground 105 | playground/ 106 | 107 | pytest-report\.csv 108 | *.cppimporthash 109 | .rendered.* 110 | Databases.db 111 | 112 | # Node.js 113 | dist/ 114 | node_modules/ 115 | coverage/ 116 | 117 | # Generated by Cargo 118 | # will have compiled files and executables 119 | /target/ 120 | 121 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 122 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 123 | Cargo.lock 124 | 125 | # These are backup files generated by rustfmt 126 | **/*.rs.bk 127 | -------------------------------------------------------------------------------- /braveblock/__init__.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | import sys 3 | import importlib.resources 4 | import typing 5 | 6 | from . import braveblock 7 | 8 | PY_VERSION_MAJOR = sys.version_info.major 9 | PY_VERSION_MINOR = sys.version_info.minor 10 | 11 | 12 | class Adblocker: 13 | 14 | @staticmethod 15 | def read_gz_binary( 16 | file_name: str 17 | ) -> bytes: 18 | if PY_VERSION_MAJOR < 3 or PY_VERSION_MINOR < 11: 19 | return importlib.resources.read_binary( 20 | package=__package__, 21 | resource=file_name, 22 | ) 23 | with importlib.resources.files( 24 | __package__, 25 | ).joinpath( 26 | file_name, 27 | ).open( 28 | 'rb', 29 | ) as _word_frequencies_binary: 30 | return _word_frequencies_binary.read() 31 | 32 | def __init__( 33 | self, 34 | rules: typing.Optional[typing.List[str]] = None, 35 | include_easylist: bool = True, 36 | include_easyprivacy: bool = True, 37 | ) -> None: 38 | combined_rules = [] 39 | 40 | if rules is not None: 41 | combined_rules.extend( 42 | rules 43 | ) 44 | 45 | if include_easylist: 46 | easylist_compressed_data = self.read_gz_binary( 47 | file_name='easylist.txt.gz', 48 | ) 49 | easylist_data = gzip.decompress( 50 | easylist_compressed_data 51 | ).decode() 52 | easylist_rules = easylist_data.splitlines() 53 | combined_rules.extend( 54 | easylist_rules 55 | ) 56 | 57 | if include_easyprivacy: 58 | easyprivacy_compressed_data = self.read_gz_binary( 59 | file_name='easyprivacy.txt.gz', 60 | ) 61 | 62 | easyprivacy_data = gzip.decompress( 63 | easyprivacy_compressed_data 64 | ).decode() 65 | easyprivacy_rules = easyprivacy_data.splitlines() 66 | combined_rules.extend( 67 | easyprivacy_rules 68 | ) 69 | 70 | self.engine = braveblock.Adblocker( 71 | rules=combined_rules, 72 | ) 73 | 74 | def check_network_urls( 75 | self, 76 | url: str, 77 | source_url: str, 78 | request_type: str, 79 | ) -> bool: 80 | return self.engine.check_network_urls( 81 | url=url, 82 | source_url=source_url, 83 | request_type=request_type, 84 | ) 85 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use adblock::engine::Engine; 2 | use adblock::lists::ParseOptions; 3 | use pyo3::prelude::*; 4 | 5 | /// Adblocker class 6 | /// Hold the adblocker engine loaded with the rules 7 | /// 8 | /// input: 9 | /// rules: List[str] -> list of strings that represent the rules to be applied 10 | /// 11 | /// example: 12 | /// braveblock.Adblocker( 13 | /// rules=[ 14 | /// "-advertisement-icon.", 15 | /// "-advertisement/script.", 16 | /// ] 17 | /// ) 18 | #[pyclass] 19 | struct Adblocker { 20 | engine: Engine, 21 | } 22 | 23 | #[pymethods] 24 | impl Adblocker { 25 | #[new] 26 | fn new(rules: Vec) -> Self { 27 | Adblocker { engine: Engine::from_rules(&rules, ParseOptions::default()) } 28 | } 29 | 30 | /// The function that should tell whether a specific request should be blocked according to the loaded rules 31 | /// 32 | /// input: 33 | /// url: str -> The inspected url that should be tested 34 | /// source_url: str -> The source url that made the request to the inspected url 35 | /// request_type: str -> The type of the resource that is being requested. Can be one of the following: 36 | /// "beacon", "csp_report", "document", "font", "image", "imageset", "main_frame", 37 | /// "media", "object_subrequest", "object", "other", "ping", "script", "speculative", 38 | /// "stylesheet", "sub_frame", "subdocument", "web_manifest", "websocket", "xbl", 39 | /// "xhr", "xml_dtd", "xmlhttprequest", "xslt" 40 | /// 41 | /// returns: 42 | /// bool -> Whether the request should be blocked or not 43 | /// 44 | /// example: 45 | /// adblocker.check_network_urls( 46 | /// url="http://example.com/-advertisement-icon.", 47 | /// source_url="http://example.com/", 48 | /// request_type="image", 49 | /// ) 50 | fn check_network_urls( 51 | &mut self, 52 | url: &str, 53 | source_url: &str, 54 | request_type: &str, 55 | ) -> PyResult { 56 | let blocker_result = self.engine.check_network_urls( 57 | url, 58 | source_url, 59 | request_type 60 | ); 61 | 62 | Ok(blocker_result.matched) 63 | } 64 | } 65 | 66 | /// Braveblock is a python library that implemented an adblocker based on Brave's browser adblocker written in Rust 67 | /// This library is a bindings for the original adblocker written by Brave's team 68 | /// Original code can be found here https://github.com/brave/adblock-rust 69 | #[pymodule] 70 | fn braveblock(_py: Python, m: &PyModule) -> PyResult<()> { 71 | m.add_class::()?; 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /tests/test_braveblock.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import braveblock 4 | 5 | 6 | class BraveblockTestCase( 7 | unittest.TestCase, 8 | ): 9 | def test_adblocker_default_rules( 10 | self, 11 | ): 12 | adblocker = braveblock.Adblocker() 13 | 14 | self.assertFalse( 15 | expr=adblocker.check_network_urls( 16 | url='http://some-website.com/image.jpg', 17 | source_url='http://some-website.com/', 18 | request_type='image', 19 | ), 20 | ) 21 | self.assertTrue( 22 | expr=adblocker.check_network_urls( 23 | url='http://some-google-ads.com/image.jpg', 24 | source_url='http://some-google-ads.com/', 25 | request_type='image', 26 | ), 27 | ) 28 | self.assertTrue( 29 | expr=adblocker.check_network_urls( 30 | url='http://some-google-analytics.com/image.jpg', 31 | source_url='http://some-google-analytics.com/', 32 | request_type='image', 33 | ), 34 | ) 35 | 36 | def test_adblocker_custom_rules( 37 | self, 38 | ): 39 | adblocker = braveblock.Adblocker( 40 | rules=[ 41 | '-advertisement-icon.', 42 | ], 43 | include_easylist=False, 44 | include_easyprivacy=False, 45 | ) 46 | 47 | self.assertTrue( 48 | expr=adblocker.check_network_urls( 49 | url='http://domain.com/some-advertisement-icon.', 50 | source_url='http://domain.com/', 51 | request_type='image', 52 | ), 53 | ) 54 | self.assertFalse( 55 | expr=adblocker.check_network_urls( 56 | url='http://domain.com/other-icon.ico', 57 | source_url='http://domain.com/', 58 | request_type='image', 59 | ), 60 | ) 61 | 62 | self.assertFalse( 63 | expr=adblocker.check_network_urls( 64 | url='http://some-website.com/image.jpg', 65 | source_url='http://some-website.com/', 66 | request_type='image', 67 | ), 68 | ) 69 | 70 | self.assertFalse( 71 | expr=adblocker.check_network_urls( 72 | url='http://google-ads.com/image.jpg', 73 | source_url='http://google-ads.com/', 74 | request_type='image', 75 | ), 76 | ) 77 | self.assertFalse( 78 | expr=adblocker.check_network_urls( 79 | url='http://some-google-analytics.com/image.jpg', 80 | source_url='http://some-google-analytics.com/', 81 | request_type='image', 82 | ), 83 | ) 84 | 85 | def test_adblocker_custom_rules_plus_default( 86 | self, 87 | ): 88 | adblocker = braveblock.Adblocker( 89 | rules=[ 90 | '-advertisement-icon.', 91 | ], 92 | ) 93 | 94 | self.assertTrue( 95 | expr=adblocker.check_network_urls( 96 | url='http://legitimate.com/some-advertisement-icon.', 97 | source_url='http://legitimate.com/', 98 | request_type='image', 99 | ), 100 | ) 101 | self.assertFalse( 102 | expr=adblocker.check_network_urls( 103 | url='http://legitimate.com/other-icon.ico', 104 | source_url='http://legitimate.com/', 105 | request_type='image', 106 | ), 107 | ) 108 | 109 | self.assertFalse( 110 | expr=adblocker.check_network_urls( 111 | url='http://legitimate.com/image.jpg', 112 | source_url='http://legitimate.com/', 113 | request_type='image', 114 | ), 115 | ) 116 | 117 | self.assertTrue( 118 | expr=adblocker.check_network_urls( 119 | url='http://google-ads.com/image.jpg', 120 | source_url='http://google-ads.com/', 121 | request_type='image', 122 | ), 123 | ) 124 | self.assertTrue( 125 | expr=adblocker.check_network_urls( 126 | url='http://some-google-analytics.com/image.jpg', 127 | source_url='http://some-google-analytics.com/', 128 | request_type='image', 129 | ), 130 | ) 131 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "colorama" 3 | version = "0.4.6" 4 | description = "Cross-platform colored terminal text." 5 | category = "dev" 6 | optional = false 7 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 8 | 9 | [[package]] 10 | name = "exceptiongroup" 11 | version = "1.2.0" 12 | description = "Backport of PEP 654 (exception groups)" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=3.7" 16 | 17 | [package.extras] 18 | test = ["pytest (>=6)"] 19 | 20 | [[package]] 21 | name = "importlib-metadata" 22 | version = "6.7.0" 23 | description = "Read metadata from Python packages" 24 | category = "dev" 25 | optional = false 26 | python-versions = ">=3.7" 27 | 28 | [package.dependencies] 29 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 30 | zipp = ">=0.5" 31 | 32 | [package.extras] 33 | docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"] 34 | perf = ["ipython"] 35 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-ruff", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] 36 | 37 | [[package]] 38 | name = "iniconfig" 39 | version = "2.0.0" 40 | description = "brain-dead simple config-ini parsing" 41 | category = "dev" 42 | optional = false 43 | python-versions = ">=3.7" 44 | 45 | [[package]] 46 | name = "maturin" 47 | version = "1.4.0" 48 | description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" 49 | category = "dev" 50 | optional = false 51 | python-versions = ">=3.7" 52 | 53 | [package.dependencies] 54 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 55 | 56 | [package.extras] 57 | zig = ["ziglang (>=0.10.0,<0.11.0)"] 58 | patchelf = ["patchelf"] 59 | 60 | [[package]] 61 | name = "packaging" 62 | version = "23.2" 63 | description = "Core utilities for Python packages" 64 | category = "dev" 65 | optional = false 66 | python-versions = ">=3.7" 67 | 68 | [[package]] 69 | name = "pluggy" 70 | version = "1.2.0" 71 | description = "plugin and hook calling mechanisms for python" 72 | category = "dev" 73 | optional = false 74 | python-versions = ">=3.7" 75 | 76 | [package.dependencies] 77 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 78 | 79 | [package.extras] 80 | dev = ["pre-commit", "tox"] 81 | testing = ["pytest", "pytest-benchmark"] 82 | 83 | [[package]] 84 | name = "pytest" 85 | version = "7.4.4" 86 | description = "pytest: simple powerful testing with Python" 87 | category = "dev" 88 | optional = false 89 | python-versions = ">=3.7" 90 | 91 | [package.dependencies] 92 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 93 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 94 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 95 | iniconfig = "*" 96 | packaging = "*" 97 | pluggy = ">=0.12,<2.0" 98 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 99 | 100 | [package.extras] 101 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 102 | 103 | [[package]] 104 | name = "pytest-runner" 105 | version = "6.0.1" 106 | description = "Invoke py.test as distutils command with dependency resolution" 107 | category = "dev" 108 | optional = false 109 | python-versions = ">=3.7" 110 | 111 | [package.extras] 112 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] 113 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-virtualenv", "types-setuptools", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] 114 | 115 | [[package]] 116 | name = "tomli" 117 | version = "2.0.1" 118 | description = "A lil' TOML parser" 119 | category = "dev" 120 | optional = false 121 | python-versions = ">=3.7" 122 | 123 | [[package]] 124 | name = "typing-extensions" 125 | version = "4.7.1" 126 | description = "Backported and Experimental Type Hints for Python 3.7+" 127 | category = "dev" 128 | optional = false 129 | python-versions = ">=3.7" 130 | 131 | [[package]] 132 | name = "zipp" 133 | version = "3.15.0" 134 | description = "Backport of pathlib-compatible object wrapper for zip files" 135 | category = "dev" 136 | optional = false 137 | python-versions = ">=3.7" 138 | 139 | [package.extras] 140 | docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"] 141 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "jaraco.functools", "more-itertools", "big-o", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"] 142 | 143 | [metadata] 144 | lock-version = "1.1" 145 | python-versions = "^3.7" 146 | content-hash = "d3751775f5a48f55874329689185792d15525d44f15678cc3bfeb66b5dea0d3d" 147 | 148 | [metadata.files] 149 | colorama = [] 150 | exceptiongroup = [] 151 | importlib-metadata = [] 152 | iniconfig = [] 153 | maturin = [] 154 | packaging = [] 155 | pluggy = [] 156 | pytest = [] 157 | pytest-runner = [] 158 | tomli = [] 159 | typing-extensions = [] 160 | zipp = [] 161 | --------------------------------------------------------------------------------