├── .github └── workflows │ ├── docs.yml │ └── release.yaml ├── .gitignore ├── LICENSE ├── README.md ├── README.zh-CN.md ├── assets └── solid.svg ├── docs ├── .vitepress │ └── config.ts ├── index.md ├── privacy.md └── public │ ├── .nojekyll │ ├── CNAME │ ├── disable.png │ ├── enable.png │ └── icon.png ├── entrypoints ├── __tests__ │ └── background.test.ts ├── background.ts ├── popup │ ├── App.tsx │ ├── DangerExt.css │ ├── DangerExt.tsx │ ├── HomeView.css │ ├── HomeView.tsx │ ├── index.html │ ├── main.tsx │ └── style.css └── utils │ ├── isDanger.ts │ └── matchesPattern.ts ├── package.json ├── pnpm-lock.yaml ├── public ├── icon │ ├── 128.png │ ├── 16.png │ ├── 32.png │ ├── 48.png │ └── 96.png └── wxt.svg ├── tsconfig.json ├── vitest.config.ts └── wxt.config.ts /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | on: 2 | # Runs on pushes targeting the `main` branch. Change this to `master` if you're 3 | # using the `master` branch as the default branch. 4 | push: 5 | branches: [main] 6 | paths: 7 | - 'docs/**' 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: read 14 | pages: write 15 | id-token: write 16 | 17 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 18 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 19 | concurrency: 20 | group: pages 21 | cancel-in-progress: false 22 | 23 | jobs: 24 | # Build job 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v3 30 | with: 31 | fetch-depth: 0 # Not needed if lastUpdated is not enabled 32 | # - uses: pnpm/action-setup@v2 # Uncomment this if you're using pnpm 33 | # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 34 | - uses: pnpm/action-setup@v2 35 | with: 36 | version: 8 37 | - name: Setup Node 38 | uses: actions/setup-node@v3 39 | with: 40 | node-version: 20 41 | cache: pnpm # or pnpm / yarn 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v3 44 | - name: Install dependencies 45 | run: pnpm i # or pnpm install / yarn install / bun install 46 | - name: Build 47 | run: | 48 | pnpm docs:build 49 | - name: Upload artifact 50 | uses: actions/upload-pages-artifact@v2 51 | with: 52 | path: docs/.vitepress/dist 53 | 54 | # Deployment job 55 | deploy: 56 | environment: 57 | name: github-pages 58 | url: ${{ steps.deployment.outputs.page_url }} 59 | needs: build 60 | runs-on: ubuntu-latest 61 | name: Deploy 62 | steps: 63 | - name: Deploy to GitHub Pages 64 | id: deployment 65 | uses: actions/deploy-pages@v2 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | DIRECTORY: .output 3 | PROJECT_NAME: secure-browse 4 | 5 | name: Deploy 6 | 7 | on: 8 | push: 9 | branches: ['main'] 10 | pull_request: 11 | branches: ['main'] 12 | 13 | jobs: 14 | Version: 15 | runs-on: ubuntu-latest 16 | outputs: 17 | version: ${{ steps.get_version.outputs.version }} 18 | version_changed: ${{ steps.check_version.outputs.version_changed }} 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - name: Check for version change 24 | id: check_version 25 | run: | 26 | if git diff --name-only HEAD~1..HEAD | grep -q '^package\.json$'; then 27 | VERSION_CHANGED=1 28 | else 29 | VERSION_CHANGED=0 30 | fi 31 | echo "version_changed=$VERSION_CHANGED" >> "$GITHUB_OUTPUT" 32 | - name: Get version 33 | if: ${{ steps.check_version.outputs.version_changed == '1' }} 34 | id: get_version 35 | run: | 36 | VERSION=$(jq -r .version package.json) 37 | echo "version=$VERSION" >> "$GITHUB_OUTPUT" 38 | 39 | Submit: 40 | permissions: 41 | contents: write 42 | needs: Version 43 | if: ${{ needs.Version.outputs.version_changed == '1' }} 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/checkout@v4 47 | - uses: pnpm/action-setup@v3 48 | with: 49 | version: 'latest' 50 | - name: Use Node.js 51 | uses: actions/setup-node@v4 52 | with: 53 | node-version: 20 54 | cache: 'pnpm' 55 | - name: Install dependencies 56 | run: pnpm install 57 | - name: Zip extensions 58 | run: | 59 | pnpm run zip && 60 | pnpm run zip:firefox 61 | - name: Create Release 62 | uses: softprops/action-gh-release@v2 63 | with: 64 | tag_name: 'v${{ needs.Version.outputs.version }}' 65 | name: 'v${{ needs.Version.outputs.version }}' 66 | draft: false 67 | prerelease: false 68 | files: | 69 | ${{ env.DIRECTORY }}/${{env.PROJECT_NAME}}-${{ needs.Version.outputs.version }}-chrome.zip 70 | # ${{ env.DIRECTORY }}/${{env.PROJECT_NAME}}-${{ needs.Version.outputs.version }}-firefox.zip 71 | # ${{ env.DIRECTORY }}/${{env.PROJECT_NAME}}-${{ needs.Version.outputs.version }}-sources.zip 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .output 12 | stats.html 13 | stats-*.json 14 | .wxt 15 | web-ext.config.ts 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | cache 28 | dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 rxliuli 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 | <<<<<<< HEAD 22 | SOFTWARE. 23 | ======= 24 | SOFTWARE. 25 | >>>>>>> dev 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Secure Browse 2 | 3 | ## Overview 4 | 5 | **Secure Browse** is a Chrome extension designed to enhance your online security by managing other extensions based on the websites you visit. This extension automatically disables non-whitelisted extensions when you visit financial websites, ensuring a safer browsing experience. When you navigate away from these sites, it re-enables the previously disabled extensions. 6 | 7 | ## Features 8 | 9 | - **Automatic Extension Management**: Automatically disables potentially unsafe extensions when visiting financial sites. 10 | - **Custom Whitelist**: Maintain a whitelist of trusted extensions that remain active on financial websites. 11 | - **Seamless Browsing**: Extensions are re-enabled when you navigate away from financial sites. 12 | - **User-Friendly**: No manual intervention required; the extension works silently in the background. 13 | 14 | ## Current Whitelist 15 | 16 | The following extensions are included in the default whitelist and will remain enabled when you visit financial websites: 17 | 18 | - **uBlock Origin: cjpalhdlnbpafiamejdnhcphjbkeiagm** 19 | 20 | ## Supported Financial Websites 21 | 22 | The extension currently monitors and protects your browsing on the following financial websites: 23 | 24 | - `*.binance.com` 25 | - `*.coinbase.com` 26 | - `*.kraken.com` 27 | 28 | ## Installation 29 | 30 | 1. **Download the Extension**: Clone or download the extension from the repository. 31 | 2. **Load the Extension**: 32 | - Open Chrome and navigate to `chrome://extensions/`. 33 | - Enable Developer mode by toggling the switch in the top right corner. 34 | - Click on "Load unpacked" and select the extension directory. 35 | 36 | ## Contributing 37 | 38 | We welcome contributions from the community! If you have suggestions for new features or improvements, please submit a pull request or open an issue. 39 | 40 | ## License 41 | 42 | This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details. 43 | 44 | ## Support 45 | 46 | If you encounter any issues or have any questions, please open an issue in the GitHub repository. 47 | 48 | ## Screenshots 49 | 50 | ![Screenshot 1](./docs/public/enable.png) 51 | ![Screenshot 2](./docs/public/disable.png) 52 | 53 | ## FAQ 54 | 55 | ### Why is Firefox not supported? 56 | 57 | Secure Browse currently supports only the Chrome browser because the extension relies on specific Chrome APIs to manage and control other extensions. These APIs are not available in Firefox, making it impossible to achieve the same functionality. We are keeping an eye on the development of Firefox APIs and will consider supporting Firefox when feasible. 58 | 59 | Key APIs that are either nonexistent or unavailable in Firefox: 60 | 61 | - `browser.management.setEnabled`: It is not possible to disable regular extensions; only the enable/disable state of theme extensions can be modified. Reference: [Bug 1282982](https://bugzilla.mozilla.org/show_bug.cgi?id=1282982) 62 | - `chrome.management.uninstall`: This API, which allows uninstalling malicious extensions from a blacklist, does not exist in Firefox. Reference: [management.uninstall() - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/management/uninstall) 63 | 64 | ## Contact 65 | 66 | For any inquiries, please contact us at [rxliuli@gmail.com](mailto:rxliuli@gmail.com). 67 | 68 | --- 69 | 70 | By using **Secure Browse**, you can ensure a safer and more secure browsing experience on financial websites. Download and install today to take control of your Chrome extensions! 71 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Secure Browse 2 | 3 | ## 概述 4 | 5 | **Secure Browse** 是一款 Chrome 扩展,旨在通过根据您访问的网站来管理其他扩展,从而增强您的在线安全性。该扩展在您访问金融网站时会自动禁用不在白名单中的扩展,从而确保更安全的浏览体验。当您离开这些网站时,它会重新启用之前禁用的扩展。 6 | 7 | ## 功能 8 | 9 | - **自动扩展管理**:在访问金融网站时自动禁用潜在不安全的扩展。 10 | - **自定义白名单**:维护一个可信扩展的白名单,使其在金融网站上保持启用。 11 | - **无缝浏览**:当您离开金融网站时,扩展会重新启用。 12 | - **用户友好**:无需手动干预;扩展在后台静默运行。 13 | 14 | ## 当前白名单 15 | 16 | 以下扩展包含在默认白名单中,并在您访问金融网站时保持启用: 17 | 18 | - **uBlock Origin: cjpalhdlnbpafiamejdnhcphjbkeiagm** 19 | 20 | ## 支持的金融网站 21 | 22 | 该扩展当前监控并保护您在以下金融网站上的浏览: 23 | 24 | - `*.binance.com` 25 | - `*.coinbase.com` 26 | - `*.kraken.com` 27 | 28 | ## 安装 29 | 30 | 从 Chrome 扩展商店安装: 31 | 32 | ## 贡献 33 | 34 | 我们欢迎社区的贡献!如果您有新的功能建议或改进意见,请提交拉取请求或打开问题。 35 | 36 | ## 许可证 37 | 38 | 该项目采用 MIT 许可证。详情请参阅 [LICENSE](LICENSE) 文件。 39 | 40 | ## 支持 41 | 42 | 如果您遇到任何问题或有任何疑问,请在 GitHub 仓库中打开问题。 43 | 44 | ## 截图 45 | 46 | ![Screenshot 1](./docs/public/enable.png) 47 | ![Screenshot 2](./docs/public/disable.png) 48 | 49 | ## 常见问题 (FAQ) 50 | 51 | ### 为什么不支持 Firefox? 52 | 53 | Secure Browse 目前仅支持 Chrome 浏览器,这是因为该扩展依赖于 Chrome 的特定 API 来管理和控制其他扩展。这些 API 在 Firefox 中不可用,因此无法实现相同的功能。我们正在关注 Firefox 的 API 发展,并将在可能的情况下考虑支持 Firefox。 54 | 55 | 依赖的关键 API 在 Firefox 尚不存在或不可用 56 | 57 | - `browser.management.setEnabled`: 无法禁用普通插件,只能修改主题插件的启用状态。参考: 58 | - `chrome.management.uninstall`: 卸载黑名单中的恶意插件,该 API 在 Firefox 中不存在。参考: 59 | 60 | ## 联系方式 61 | 62 | 如有任何疑问,请通过 [rxliuli@gmail.com](mailto:rxliuli@gmail.com) 联系我们。 63 | 64 | --- 65 | 66 | 通过使用 **Secure Browse**,您可以确保在金融网站上拥有更安全和更可靠的浏览体验。立即下载并安装,以便更好地控制您的 Chrome 扩展! 67 | -------------------------------------------------------------------------------- /assets/solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | export default defineConfig({ 4 | title: 'Secure Browse', 5 | description: 'Extension Manager for Financial Sites', 6 | themeConfig: { 7 | logo: '/icon.png', 8 | nav: [ 9 | { 10 | text: 'Privacy Policy', 11 | link: '/privacy', 12 | }, 13 | { 14 | text: 'GitHub', 15 | link: 'https://github.com/rxliuli/secure-browse', 16 | }, 17 | ], 18 | footer: { 19 | message: 'Released under the MIT License.', 20 | copyright: 'Copyright © 2024 rxliuli', 21 | }, 22 | }, 23 | }) 24 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | title: Secure Browse 4 | 5 | hero: 6 | name: Secure Browse 7 | text: Extension Manager for Financial Sites 8 | image: 9 | src: /disable.png 10 | alt: Secure Browse 11 | actions: 12 | - theme: brand 13 | text: Install Extension 14 | link: https://chromewebstore.google.com/detail/klfaejodhhokdkdnbgkjeoahnaoihjfk 15 | - theme: alt 16 | text: Star on GitHub 17 | link: https://github.com/rxliuli/secure-browse 18 | --- 19 | 20 | ## Overview 21 | 22 | **Secure Browse** is a Chrome extension designed to enhance your online security by managing other extensions based on the websites you visit. This extension automatically disables non-whitelisted extensions when you visit financial websites, ensuring a safer browsing experience. When you navigate away from these sites, it re-enables the previously disabled extensions. 23 | 24 | 25 | 26 | ## Features 27 | 28 | - **Automatic Extension Management**: Automatically disables potentially unsafe extensions when visiting financial sites. 29 | - **Custom Whitelist**: Maintain a whitelist of trusted extensions that remain active on financial websites. 30 | - **Seamless Browsing**: Extensions are re-enabled when you navigate away from financial sites. 31 | - **User-Friendly**: No manual intervention required; the extension works silently in the background. 32 | 33 | ## Current Whitelist 34 | 35 | The following extensions are included in the default whitelist and will remain enabled when you visit financial websites: 36 | 37 | - **uBlock Origin: cjpalhdlnbpafiamejdnhcphjbkeiagm** 38 | 39 | ## Supported Financial Websites 40 | 41 | The extension currently monitors and protects your browsing on the following financial websites: 42 | 43 | - `*.binance.com` 44 | - `*.coinbase.com` 45 | - `*.kraken.com` 46 | 47 | ## Installation 48 | 49 | 1. **Download the Extension**: Clone or download the extension from the repository. 50 | 2. **Load the Extension**: 51 | - Open Chrome and navigate to `chrome://extensions/`. 52 | - Enable Developer mode by toggling the switch in the top right corner. 53 | - Click on "Load unpacked" and select the extension directory. 54 | 55 | ## Contributing 56 | 57 | We welcome contributions from the community! If you have suggestions for new features or improvements, please submit a pull request or open an issue. 58 | 59 | ## License 60 | 61 | This project is licensed under the MIT License. See the [LICENSE](https://github.com/rxliuli/secure-browse/blob/main/LICENSE) file for details. 62 | 63 | ## Support 64 | 65 | If you encounter any issues or have any questions, please open an issue in the GitHub repository. 66 | 67 | ## Screenshots 68 | 69 | ![Screenshot 1](/enable.png) 70 | ![Screenshot 2](/disable.png) 71 | 72 | ## FAQ 73 | 74 | ### Why is Firefox not supported? 75 | 76 | Secure Browse currently supports only the Chrome browser because the extension relies on specific Chrome APIs to manage and control other extensions. These APIs are not available in Firefox, making it impossible to achieve the same functionality. We are keeping an eye on the development of Firefox APIs and will consider supporting Firefox when feasible. 77 | 78 | Key APIs that are either nonexistent or unavailable in Firefox: 79 | 80 | - `browser.management.setEnabled`: It is not possible to disable regular extensions; only the enable/disable state of theme extensions can be modified. Reference: [Bug 1282982](https://bugzilla.mozilla.org/show_bug.cgi?id=1282982) 81 | - `chrome.management.uninstall`: This API, which allows uninstalling malicious extensions from a blacklist, does not exist in Firefox. Reference: [management.uninstall() - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/management/uninstall) 82 | 83 | ## Contact 84 | 85 | For any inquiries, please contact us at [rxliuli@gmail.com](mailto:rxliuli@gmail.com). 86 | 87 | --- 88 | 89 | By using **Secure Browse**, you can ensure a safer and more secure browsing experience on financial websites. Download and install today to take control of your Chrome extensions! 90 | -------------------------------------------------------------------------------- /docs/privacy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ## Introduction 4 | 5 | Welcome to our Chrome extension. We are committed to protecting your privacy and ensuring the security of your personal information. This Privacy Policy explains how we collect, use, and protect your information when you use our Chrome extension. 6 | 7 | ## Information We Collect 8 | 9 | Our Chrome extension collects the following types of information: 10 | 11 | 1. **Browsing Data**: We collect the URLs of the websites you visit to determine if they match our predefined list of financial sites. This is necessary to enable or disable other extensions for your safety and privacy. 12 | 2. **Extension Data**: We access information about the extensions installed in your browser, including their status (enabled or disabled) and ID. This helps us manage the extensions according to your preferences and our security protocols. 13 | 14 | ## How We Use Your Information 15 | 16 | We use the collected information for the following purposes: 17 | 18 | 1. **Security and Privacy**: To disable non-whitelisted extensions when you visit financial websites, ensuring your sensitive information remains protected from potentially malicious extensions. 19 | 2. **Extension Management**: To enable or disable extensions based on your browsing activity and predefined rules, enhancing your overall browsing experience. 20 | 21 | ## Information Sharing and Disclosure 22 | 23 | We do not share, sell, or distribute your personal information to third parties. Your data is used solely within the context of the extension to provide the features and functionalities described above. 24 | 25 | ## User Choices 26 | 27 | You have control over the information we collect and how it is used. You can: 28 | 29 | - **Disable the Extension**: You can disable or uninstall the extension at any time through your browser's settings. 30 | - **Manage Permissions**: Adjust the permissions granted to the extension through your browser's extensions settings. 31 | 32 | ## Changes to This Privacy Policy 33 | 34 | We may update this Privacy Policy from time to time to reflect changes in our practices or legal requirements. We will notify you of any significant changes by posting the new Privacy Policy on our website and updating the "Last Updated" date at the top of this document. 35 | 36 | ## Contact Us 37 | 38 | If you have any questions or concerns about this Privacy Policy or our data practices, please contact us at: 39 | 40 | - **Email**: [rxliuli@gmail.com] 41 | 42 | --- 43 | 44 | By using our Chrome extension, you acknowledge that you have read and understood this Privacy Policy and agree to its terms. 45 | 46 | Thank you for trusting us with your information. 47 | 48 | --- 49 | 50 | Feel free to customize this privacy policy to better fit your specific extension and practices. 51 | -------------------------------------------------------------------------------- /docs/public/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/docs/public/.nojekyll -------------------------------------------------------------------------------- /docs/public/CNAME: -------------------------------------------------------------------------------- 1 | secure-browse.rxliuli.com -------------------------------------------------------------------------------- /docs/public/disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/docs/public/disable.png -------------------------------------------------------------------------------- /docs/public/enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/docs/public/enable.png -------------------------------------------------------------------------------- /docs/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/docs/public/icon.png -------------------------------------------------------------------------------- /entrypoints/__tests__/background.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { matchesPattern } from '../utils/matchesPattern' 3 | import { config } from '../background' 4 | 5 | it('matchesPattern', () => { 6 | config.financialSites.forEach((it) => { 7 | it.examples?.forEach((url) => { 8 | expect(matchesPattern(url, [it.match])).true 9 | }) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /entrypoints/background.ts: -------------------------------------------------------------------------------- 1 | import { Management, Tabs } from 'wxt/browser' 2 | import { isDangerExt } from './utils/isDanger' 3 | import { matchesPattern } from './utils/matchesPattern' 4 | 5 | interface WetInfo { 6 | name: string 7 | id: string 8 | } 9 | 10 | interface SiteInfo { 11 | match: string 12 | examples?: string[] 13 | } 14 | 15 | interface Config { 16 | whiteExtList: WetInfo[] 17 | financialSites: SiteInfo[] 18 | } 19 | 20 | export const config: Config = { 21 | whiteExtList: [ 22 | { 23 | name: 'uBlock Origin', 24 | id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm', 25 | }, 26 | ], 27 | financialSites: [ 28 | { 29 | match: '*://*.binance.com/*', 30 | examples: [ 31 | 'https://www.binance.com/en/trade/BTC_USDT', 32 | 'https://www.binance.com/en/trade/ETH_BTC', 33 | ], 34 | }, 35 | { match: '*://*.coinbase.com/*', examples: ['https://www.coinbase.com/'] }, 36 | { match: '*://*.kraken.com/*', examples: ['https://www.kraken.com/'] }, 37 | // ref: https://github.com/Tampermonkey/tampermonkey/blob/07f668cd1cabb2939220045839dec4d95d2db0c8/src/background.js#L2414 38 | { 39 | match: '*paypal.com/*', 40 | examples: ['https://www.paypal.com/', 'https://www.paypal.com/c2/home'], 41 | }, 42 | { 43 | match: '*stripe.com/*', 44 | examples: [ 45 | 'https://stripe.com/', 46 | 'https://dashboard.stripe.com/login', 47 | 'https://dashboard.stripe.com/payments', 48 | ], 49 | }, 50 | { match: '*://plusone.google.com/*/fastbutton*' }, 51 | { match: '*://platform.twitter.com/widgets/*' }, 52 | { match: 'https://*bankamerica.tld/*' }, 53 | { match: 'https://*deutsche-bank-24.tld/*' }, 54 | { match: 'https://*bankofamerica.tld/*' }, 55 | { match: '*://www.facebook.com/plugins/*' }, 56 | { match: '*://*.htx.com/*', examples: [ 'https://www.htx.com/' ] }, 57 | { match: '*://*.okx.com/*', examples: [ 'https://www.okx.com/' ] }, 58 | { match: '*://*.bitfinex.com/*', examples: [ 'https://www.bitfinex.com/' ] }, 59 | ], 60 | } 61 | 62 | const whiteList = config.whiteExtList.map((it) => it.id) 63 | 64 | const financialSites = config.financialSites.map((it) => it.match) 65 | 66 | let extensionsList: Management.ExtensionInfo[] = [] 67 | async function updateExtensionsList() { 68 | extensionsList = await browser.management.getAll() 69 | } 70 | 71 | let disabledExtensions: string[] = [] 72 | 73 | async function persistData() { 74 | await browser.storage.local.set({ 75 | disabledExtensions, 76 | }) 77 | } 78 | 79 | // Function to disable extensions not in the whitelist 80 | async function disableExtensions() { 81 | const disableList = extensionsList.filter( 82 | (it) => 83 | !whiteList.includes(it.id) && it.enabled && it.id !== browser.runtime.id, 84 | ) 85 | if (disableList.length === 0) { 86 | return 87 | } 88 | disabledExtensions = disableList.map((it) => it.id) 89 | await Promise.all( 90 | disableList.map(async (it) => { 91 | await browser.management.setEnabled(it.id, false) 92 | console.log(`Disabled extension: ${it.name}`) 93 | }), 94 | ) 95 | await persistData() 96 | } 97 | 98 | // Function to re-enable previously disabled extensions 99 | async function enableExtensions() { 100 | await Promise.all( 101 | disabledExtensions.map(async (it) => { 102 | await browser.management.setEnabled(it, true) 103 | console.log(`Re-enabled extension: ${it}`) 104 | }), 105 | ) 106 | disabledExtensions = [] 107 | await persistData() 108 | } 109 | 110 | export default defineBackground(() => { 111 | async function handleTab(tab: Tabs.Tab) { 112 | if (tab.url) { 113 | if (matchesPattern(tab.url, financialSites)) { 114 | await disableExtensions() 115 | } else { 116 | await enableExtensions() 117 | } 118 | } 119 | } 120 | 121 | // Listen for tab updates 122 | browser.tabs.onUpdated.addListener(async (_tabId, changeInfo, tab) => { 123 | if (changeInfo.status === 'loading' || changeInfo.status === 'complete') { 124 | await handleTab(tab) 125 | } 126 | }) 127 | 128 | // Listen for tab switches 129 | browser.tabs.onActivated.addListener(async (activeInfo) => { 130 | const tab = await browser.tabs.get(activeInfo.tabId) 131 | await handleTab(tab) 132 | }) 133 | 134 | // Listen for extension installation and removal to update the list 135 | updateExtensionsList() 136 | browser.management.onInstalled.addListener(updateExtensionsList) 137 | browser.management.onUninstalled.addListener(updateExtensionsList) 138 | browser.management.onEnabled.addListener(updateExtensionsList) 139 | browser.management.onDisabled.addListener(updateExtensionsList) 140 | 141 | // Restore extensions when the extension is reloaded or the browser starts 142 | browser.runtime.onStartup.addListener(async () => { 143 | disabledExtensions = (( 144 | await browser.storage.local.get('disabledExtensions') 145 | ).disabledExtensions ?? []) as string[] 146 | await enableExtensions() 147 | }) 148 | browser.runtime.onInstalled.addListener(async () => { 149 | await enableExtensions() 150 | }) 151 | 152 | browser.management.onInstalled.addListener(async (info) => { 153 | if (!isDangerExt(info)) { 154 | return 155 | } 156 | await browser.management.setEnabled(info.id, false) 157 | await browser.windows.create({ 158 | url: browser.runtime.getURL(`/popup.html?id=${info.id}`), 159 | type: 'popup', 160 | width: 400, 161 | height: 600, 162 | top: 100, 163 | focused: true, 164 | }) 165 | }) 166 | }) 167 | -------------------------------------------------------------------------------- /entrypoints/popup/App.tsx: -------------------------------------------------------------------------------- 1 | import { ExtView } from './DangerExt' 2 | import { HomeView } from './HomeView' 3 | 4 | export function App() { 5 | const hasId = new URLSearchParams(location.search).get('id') 6 | return hasId ? : 7 | } 8 | -------------------------------------------------------------------------------- /entrypoints/popup/DangerExt.css: -------------------------------------------------------------------------------- 1 | .container { 2 | /* max-width: 600px; */ 3 | /* margin: 0 auto; */ 4 | /* background: #1e1e1e; */ 5 | padding: 20px; 6 | border-radius: 8px; 7 | /* box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); */ 8 | } 9 | .header { 10 | color: #ff6f61; 11 | text-align: center; 12 | } 13 | p { 14 | font-size: 16px; 15 | } 16 | ul { 17 | list-style-type: none; 18 | padding: 0; 19 | } 20 | li { 21 | background: #333; 22 | margin: 5px 0; 23 | padding: 10px; 24 | border-radius: 4px; 25 | } 26 | .button-container { 27 | text-align: center; 28 | margin-top: 20px; 29 | } 30 | .enable-button { 31 | background-color: #ff6f61; 32 | color: white; 33 | padding: 10px 20px; 34 | border: none; 35 | border-radius: 4px; 36 | font-size: 16px; 37 | cursor: pointer; 38 | } 39 | .enable-button:hover { 40 | background-color: #e65b54; 41 | } 42 | -------------------------------------------------------------------------------- /entrypoints/popup/DangerExt.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal, onMount } from 'solid-js' 2 | import { Management } from 'wxt/browser' 3 | import './DangerExt.css' 4 | 5 | export function ExtView() { 6 | const [ext, setExt] = createSignal() 7 | onMount(async () => { 8 | const extId = new URLSearchParams(location.search).get('id') 9 | if (!extId) { 10 | throw new Error('No ext id') 11 | } 12 | const ext = await browser.management.get(extId) 13 | if (!ext) { 14 | throw new Error('No ext found') 15 | } 16 | console.log('extId', ext) 17 | setExt(ext) 18 | }) 19 | async function enableExtension() { 20 | await browser.management.setEnabled(ext()!.id, true) 21 | alert('Extension enabled') 22 | window.close() 23 | } 24 | return ( 25 |
26 |

⚠️ Extension Disabled

27 |

28 | The newly installed extension [ 29 | 30 | 34 | {ext()?.name} 35 | 36 | 37 | ] can access cookies. This is very dangerous and has been automatically 38 | disabled. 39 |

40 |

Accessed Domains:

41 |
    42 | {ext()?.hostPermissions?.map((it) => ( 43 |
  • {it}
  • 44 | ))} 45 |
46 |
47 | 50 |
51 |
52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /entrypoints/popup/HomeView.css: -------------------------------------------------------------------------------- 1 | #root { 2 | margin: 0 auto; 3 | } 4 | 5 | .item { 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | gap: 1rem; 10 | margin-bottom: 1rem; 11 | font-weight: bold; 12 | } 13 | 14 | .extension-status { 15 | margin-right: 10px; 16 | color: #fff; 17 | } 18 | 19 | .extension-status.disabled { 20 | color: #4caf50; /* 绿色 */ 21 | } 22 | 23 | .extension-status.enabled { 24 | color: #f44336; /* 红色 */ 25 | } 26 | -------------------------------------------------------------------------------- /entrypoints/popup/HomeView.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx' 2 | import { createSignal, onMount } from 'solid-js' 3 | import { Management } from 'wxt/browser' 4 | import { isDangerExt } from '../utils/isDanger' 5 | import './HomeView.css' 6 | 7 | export function HomeView() { 8 | const [extensions, setExtensions] = createSignal( 9 | [], 10 | ) 11 | const refresh = async () => { 12 | const exts = await browser.management.getAll() 13 | const dangerousExtensions = exts.filter(isDangerExt) 14 | console.log('Dangerous extensions:', dangerousExtensions) 15 | setExtensions(dangerousExtensions) 16 | } 17 | 18 | onMount(refresh) 19 | 20 | const toggleExtension = async (id: string, enabled: boolean) => { 21 | await browser.management.setEnabled(id, !enabled) 22 | await refresh() 23 | } 24 | 25 | return ( 26 |
27 |

Dangerous Extensions

28 | {extensions().map((extension) => ( 29 |
  • 36 | {extension.name} 37 | 46 |
  • 47 | ))} 48 |
    49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /entrypoints/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Secure Browse 7 | 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /entrypoints/popup/main.tsx: -------------------------------------------------------------------------------- 1 | import { render } from 'solid-js/web' 2 | 3 | import './style.css' 4 | import { App } from './App' 5 | 6 | render(() => , document.getElementById('root')!) 7 | -------------------------------------------------------------------------------- /entrypoints/popup/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | @media (prefers-color-scheme: light) { 58 | :root { 59 | color: #213547; 60 | background-color: #ffffff; 61 | } 62 | a:hover { 63 | color: #747bff; 64 | } 65 | button { 66 | background-color: #f9f9f9; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /entrypoints/utils/isDanger.ts: -------------------------------------------------------------------------------- 1 | import { Management } from 'wxt/browser' 2 | 3 | export function isDangerExt(info: Management.ExtensionInfo) { 4 | return info.permissions?.includes('cookies') 5 | } 6 | -------------------------------------------------------------------------------- /entrypoints/utils/matchesPattern.ts: -------------------------------------------------------------------------------- 1 | import { isMatch } from 'picomatch' 2 | 3 | // Utility function to match URL with patterns 4 | export function matchesPattern(url: string, patterns: string[]) { 5 | return isMatch(url, patterns, { 6 | bash: true, 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-browse", 3 | "description": "manifest.json description", 4 | "private": true, 5 | "version": "0.2.10", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "wxt", 9 | "dev:firefox": "wxt -b firefox", 10 | "build": "wxt build", 11 | "build:firefox": "wxt build -b firefox", 12 | "zip": "wxt zip", 13 | "zip:firefox": "wxt zip -b firefox", 14 | "compile": "tsc --noEmit", 15 | "postinstall": "wxt prepare", 16 | "docs:dev": "vitepress dev docs", 17 | "docs:build": "vitepress build docs", 18 | "docs:preview": "vitepress preview docs", 19 | "docs:deploy": "gh-pages -d docs/.vitepress/dist" 20 | }, 21 | "dependencies": { 22 | "clsx": "^2.1.1", 23 | "picomatch": "^4.0.2", 24 | "solid-js": "^1.8.7" 25 | }, 26 | "devDependencies": { 27 | "@types/picomatch": "^2.3.3", 28 | "gh-pages": "^6.1.1", 29 | "typescript": "^5.3.3", 30 | "vite-plugin-solid": "^2.8.0", 31 | "vitepress": "^1.2.2", 32 | "vitest": "^1.6.0", 33 | "wxt": "^0.18.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/icon/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/public/icon/128.png -------------------------------------------------------------------------------- /public/icon/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/public/icon/16.png -------------------------------------------------------------------------------- /public/icon/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/public/icon/32.png -------------------------------------------------------------------------------- /public/icon/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/public/icon/48.png -------------------------------------------------------------------------------- /public/icon/96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rxliuli/secure-browse/7532ff518b4ccdc444755fa581ab6707e1b6c0c4/public/icon/96.png -------------------------------------------------------------------------------- /public/wxt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.wxt/tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "jsxImportSource": "solid-js" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | // vitest.config.ts 2 | import { defineConfig } from 'vitest/config' 3 | import { WxtVitest } from 'wxt/testing' 4 | 5 | export default defineConfig({ 6 | plugins: [WxtVitest()], 7 | }) 8 | -------------------------------------------------------------------------------- /wxt.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'wxt' 2 | import Solid from 'vite-plugin-solid' 3 | 4 | // See https://wxt.dev/api/config.html 5 | export default defineConfig({ 6 | manifest: { 7 | name: 'Secure Browse', 8 | short_name: 'Secure Browse', 9 | permissions: ['management', 'tabs', 'storage'], 10 | description: 'Extension Manager for Financial Sites', 11 | homepage_url: 'https://secure-browse.rxliuli.com', 12 | }, 13 | manifestVersion: 3, 14 | runner: { 15 | disabled: true, 16 | }, 17 | vite: () => ({ 18 | build: { 19 | target: 'esnext', 20 | }, 21 | plugins: [Solid()], 22 | }), 23 | }) 24 | --------------------------------------------------------------------------------