diff --git a/.github/workflows/actions/build-test-pack/action.yml b/.github/workflows/actions/build-test-pack/action.yml
new file mode 100644
index 00000000..bcb28a0c
--- /dev/null
+++ b/.github/workflows/actions/build-test-pack/action.yml
@@ -0,0 +1,42 @@
+# Assumes that:
+# 1. the following env variables are set:
+# - ZIP_FILE_PATH
+# - EXTENSION_DIR
+# 2. repository checked out
+# Effects:
+# - builds and tests an extension, fails on error
+# - packed extension.zip saved to env.ZIP_FILE_PATH if inputs.doNotPackZip == 'false'
+
+name: "Build, test and pack WebExtension"
+description: "Builds, tests, and packs extension dir into zip file"
+
+inputs:
+ doNotPackZip:
+ description: 'Set `true` to omit pack step'
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ # Add additional build and test steps here
+
+ - name: Copy extension to folder
+ shell: bash
+ run: |
+ mkdir -p ${{ env.EXTENSION_DIR }}
+ cp manifest.json ${{ env.EXTENSION_DIR }}
+ cp blocked.html ${{ env.EXTENSION_DIR }}
+ cp -r config/ ${{ env.EXTENSION_DIR }}
+ cp -r images/ ${{ env.EXTENSION_DIR }}
+ cp -r options/ ${{ env.EXTENSION_DIR }}
+ cp -r popup/ ${{ env.EXTENSION_DIR }}
+ cp -r rules/ ${{ env.EXTENSION_DIR }}
+ cp -r scripts/ ${{ env.EXTENSION_DIR }}
+ cp -r styles/ ${{ env.EXTENSION_DIR }}
+
+ - name: Pack directory to zip
+ if: inputs.doNotPackZip != 'true'
+ uses: cardinalby/webext-buildtools-pack-extension-dir-action@28fdcac9860fb08555580587cab0d33afe4a341d
+ with:
+ extensionDir: ${{ env.EXTENSION_DIR }}
+ zipFilePath: ${{ env.ZIP_FILE_PATH }}
\ No newline at end of file
diff --git a/.github/workflows/actions/get-zip-asset/action.yml b/.github/workflows/actions/get-zip-asset/action.yml
new file mode 100644
index 00000000..88fb6f17
--- /dev/null
+++ b/.github/workflows/actions/get-zip-asset/action.yml
@@ -0,0 +1,67 @@
+# Assumes that:
+# 1. the following env variables are set:
+# - ZIP_ASSET_NAME
+# - ZIP_FILE_PATH
+# - ZIP_FILE_NAME
+# - EXTENSION_DIR
+# 2. repository checked out
+# Effects:
+# - extension.zip saved to env.ZIP_FILE_PATH
+# - outputs.releaseUploadUrl is set if ref_type == 'tag' and release exists
+# - extension.zip uploaded as build artifact to the job if it wasn't found in release
+
+name: "Obtain extension.zip asset"
+description: "Downloads zip asset from a release (if exists) or builds it from the scratch"
+inputs:
+ githubToken:
+ description: GitHub token
+ required: true
+outputs:
+ releaseUploadUrl:
+ description: Release upload url, if exists
+ value: ${{ steps.getRelease.outputs.upload_url }}
+runs:
+ using: "composite"
+ steps:
+ - name: Get release
+ id: getRelease
+ if: github.ref_type == 'tag'
+ uses: cardinalby/git-get-release-action@cedef2faf69cb7c55b285bad07688d04430b7ada
+ env:
+ GITHUB_TOKEN: ${{ inputs.githubToken }}
+ with:
+ tag: ${{ github.ref_name }}
+ doNotFailIfNotFound: true
+
+ - name: Find out zip asset id from assets JSON
+ if: steps.getRelease.outputs.assets
+ id: readAssetIdFromRelease
+ uses: cardinalby/js-eval-action@b34865f1d9cfdf35356013627474857cfe0d5091
+ env:
+ ASSETS_JSON: ${{ steps.getRelease.outputs.assets }}
+ ASSET_NAME: ${{ env.ZIP_ASSET_NAME }}
+ with:
+ expression: |
+ JSON.parse(env.ASSETS_JSON)
+ .find(asset => asset.name == env.ZIP_ASSET_NAME)?.id || ''
+
+ - name: Download found zip release asset
+ id: downloadZipAsset
+ if: steps.readAssetIdFromRelease.outputs.result
+ uses: cardinalby/download-release-asset-action@8fe4ec3a876fe25b72086c8de1faddfaeb6512ff
+ with:
+ token: ${{ inputs.githubToken }}
+ assetId: ${{ steps.readAssetIdFromRelease.outputs.result }}
+ targetPath: ${{ env.ZIP_FILE_PATH }}
+
+ - name: Build and pack zip
+ id: buildZip
+ if: steps.downloadZipAsset.outcome != 'success'
+ uses: ./.github/workflows/actions/build-test-pack
+
+ - name: Upload zip file artifact
+ if: steps.buildZip.outcome == 'success'
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+ with:
+ name: ${{ env.ZIP_FILE_NAME }}
+ path: ${{ env.ZIP_FILE_PATH }}
\ No newline at end of file
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 00000000..e5414762
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,32 @@
+name: Build and test
+on:
+ pull_request:
+ push:
+ branches:
+ - 'main'
+ - 'dev'
+ workflow_dispatch:
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Build, test and pack to zip
+ id: build
+ uses: ./.github/workflows/actions/build-test-pack
+ with:
+ # pack zip only for pull requests or workflow_dispatch events
+ doNotPackZip: ${{ github.event_name == 'push' && 'true' || 'false'}}
+
+ - name: Upload zip file artifact
+ if: github.event_name != 'push'
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+ with:
+ name: ${{ env.ZIP_FILE_NAME }}
+ path: ${{ env.ZIP_FILE_PATH }}
\ No newline at end of file
diff --git a/.github/workflows/build-assets-on-release.yml b/.github/workflows/build-assets-on-release.yml
new file mode 100644
index 00000000..351a95d9
--- /dev/null
+++ b/.github/workflows/build-assets-on-release.yml
@@ -0,0 +1,96 @@
+# On release published:
+# - if no built extension.zip asset attached to release, does that
+# - builds and attaches signed crx asset to release
+# - builds and attaches signed xpi asset to release
+name: Build release assets
+
+on:
+ release:
+ # Creating draft releases will not trigger it
+ types: [published]
+jobs:
+ # Find out asset id of existing extension.zip asset in a release or
+ # build and attach it to the release and use its asset id
+ ensure-zip:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ outputs:
+ zipAssetId: |
+ ${{ steps.getZipAssetId.outputs.result ||
+ steps.uploadZipAsset.outputs.id }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Find out "extension.zip" asset id from the release
+ id: getZipAssetId
+ uses: cardinalby/js-eval-action@b34865f1d9cfdf35356013627474857cfe0d5091
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ ASSETS_URL: ${{ github.event.release.assets_url }}
+ ASSET_NAME: ${{ env.ZIP_FILE_NAME }}
+ with:
+ expression: |
+ (await octokit.request("GET " + env.ASSETS_URL)).data
+ .find(asset => asset.name == env.ASSET_NAME)?.id || ''
+
+ - name: Build, test and pack
+ if: '!steps.getZipAssetId.outputs.result'
+ id: buildPack
+ uses: ./.github/workflows/actions/build-test-pack
+
+ - name: Upload "extension.zip" asset to the release
+ id: uploadZipAsset
+ if: '!steps.getZipAssetId.outputs.result'
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.ZIP_FILE_PATH }}
+ asset_name: ${{ env.ZIP_FILE_NAME }}
+ asset_content_type: application/zip
+
+ build-signed-crx-asset:
+ needs: ensure-zip
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Download zip release asset
+ uses: cardinalby/download-release-asset-action@8fe4ec3a876fe25b72086c8de1faddfaeb6512ff
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ assetId: ${{ needs.ensure-zip.outputs.zipAssetId }}
+ targetPath: ${{ env.ZIP_FILE_PATH }}
+
+ - name: Build offline crx
+ id: buildOfflineCrx
+ uses: cardinalby/webext-buildtools-chrome-crx-action@200e7173cbdb5acb91d381cf9f7a30080b025047
+ with:
+ zipFilePath: ${{ env.ZIP_FILE_PATH }}
+ crxFilePath: ${{ env.OFFLINE_CRX_FILE_PATH }}
+ privateKey: ${{ secrets.CHROME_CRX_PRIVATE_KEY }}
+
+ - name: Upload offline crx release asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.OFFLINE_CRX_FILE_PATH }}
+ asset_name: ${{ env.OFFLINE_CRX_FILE_NAME }}
+ asset_content_type: application/x-chrome-extension
+
diff --git a/.github/workflows/constants.env b/.github/workflows/constants.env
new file mode 100644
index 00000000..331cd5fa
--- /dev/null
+++ b/.github/workflows/constants.env
@@ -0,0 +1,11 @@
+EXTENSION_DIR=extension/
+BUILD_DIR=build/
+
+ZIP_FILE_NAME=extension.zip
+ZIP_FILE_PATH=${BUILD_DIR}${ZIP_FILE_NAME}
+
+WEBSTORE_CRX_FILE_NAME=extension.webstore.crx
+WEBSTORE_CRX_FILE_PATH=${BUILD_DIR}${WEBSTORE_CRX_FILE_NAME}
+
+OFFLINE_CRX_FILE_NAME=extension.offline.crx
+OFFLINE_CRX_FILE_PATH=${BUILD_DIR}${OFFLINE_CRX_FILE_NAME}
diff --git a/.github/workflows/google-refresh-token.yml b/.github/workflows/google-refresh-token.yml
new file mode 100644
index 00000000..32f188c0
--- /dev/null
+++ b/.github/workflows/google-refresh-token.yml
@@ -0,0 +1,14 @@
+name: Google Refresh Token
+on:
+ schedule:
+ - cron: '0 3 2 * *' # At 03:00 on day-of-month 2
+ workflow_dispatch:
+jobs:
+ fetchToken:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: cardinalby/google-api-fetch-token-action@24c99245e2a2494cc4c4b1037203d319a184b15b
+ with:
+ clientId: ${{ secrets.G_CLIENT_ID }}
+ clientSecret: ${{ secrets.G_CLIENT_SECRET }}
+ refreshToken: ${{ secrets.G_REFRESH_TOKEN }}
diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml
new file mode 100644
index 00000000..a9b71b99
--- /dev/null
+++ b/.github/workflows/pr_check.yml
@@ -0,0 +1,59 @@
+name: PR Branch Check
+
+on:
+ # Using pull_request_target instead of pull_request for secure handling of fork PRs
+ pull_request_target:
+ # Only run on these PR events
+ types: [opened, synchronize, reopened]
+ # Only check PRs targeting these branches
+ branches:
+ - main
+ - master
+
+permissions:
+ pull-requests: write
+ issues: write
+
+jobs:
+ check-branch:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check and Comment on PR
+ # Only process fork PRs with specific branch conditions
+ # Must be a fork AND (source is main/master OR target is main/master)
+ if: |
+ github.event.pull_request.head.repo.fork == true &&
+ ((github.event.pull_request.head.ref == 'main' || github.event.pull_request.head.ref == 'master') ||
+ (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master'))
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ let message = '';
+
+ // Check if PR is targeting main/master
+ if (context.payload.pull_request.base.ref === 'main' || context.payload.pull_request.base.ref === 'master') {
+ message += 'â ī¸ PRs cannot target the main branch directly. If you are attempting to contribute code please PR to the dev branch.\n\n';
+ }
+
+ // Check if PR is from a fork's main/master branch
+ if (context.payload.pull_request.head.repo.fork &&
+ (context.payload.pull_request.head.ref === 'main' || context.payload.pull_request.head.ref === 'master')) {
+ message += 'â ī¸ This PR cannot be merged because it originates from your fork\'s main/master branch. If you are attempting to contribute code please PR from your dev branch or another non-main/master branch.\n\n';
+ }
+
+ message += 'đ This PR will now be automatically closed due to the above rules.';
+
+ // Post the comment
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number: context.issue.number,
+ body: message
+ });
+
+ // Close the PR
+ await github.rest.pulls.update({
+ ...context.repo,
+ pull_number: context.issue.number,
+ state: 'closed'
+ });
diff --git a/.github/workflows/publish-on-chrome-webstore.yml b/.github/workflows/publish-on-chrome-webstore.yml
new file mode 100644
index 00000000..a9902dfc
--- /dev/null
+++ b/.github/workflows/publish-on-chrome-webstore.yml
@@ -0,0 +1,142 @@
+name: publish-on-chrome-web-store
+on:
+ workflow_dispatch:
+ inputs:
+ attemptNumber:
+ description: 'Attempt number'
+ required: false
+ default: '1'
+ maxAttempts:
+ description: 'Max attempts'
+ required: false
+ default: '10'
+ environment:
+ description: 'publish-on-webstore job environment'
+ required: false
+ default: ''
+jobs:
+ publish-on-webstore:
+ runs-on: ubuntu-latest
+ environment: ${{ github.event.inputs.environment }}
+ outputs:
+ result: ${{ steps.webStorePublish.outcome }}
+ releaseUploadUrl: ${{ steps.getZipAsset.outputs.releaseUploadUrl }}
+ steps:
+ - name: Get the next attempt number
+ id: getNextAttemptNumber
+ uses: cardinalby/js-eval-action@b34865f1d9cfdf35356013627474857cfe0d5091
+ env:
+ attemptNumber: ${{ github.event.inputs.attemptNumber }}
+ maxAttempts: ${{ github.event.inputs.maxAttempts }}
+ with:
+ expression: |
+ {
+ const
+ attempt = parseInt(env.attemptNumber),
+ max = parseInt(env.maxAttempts);
+ assert(attempt && max && max >= attempt);
+ return attempt < max ? attempt + 1 : '';
+ }
+
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Obtain packed zip
+ id: getZipAsset
+ uses: ./.github/workflows/actions/get-zip-asset
+ with:
+ githubToken: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Fetch Google API access token
+ id: fetchAccessToken
+ uses: cardinalby/google-api-fetch-token-action@24c99245e2a2494cc4c4b1037203d319a184b15b
+ with:
+ clientId: ${{ secrets.G_CLIENT_ID }}
+ clientSecret: ${{ secrets.G_CLIENT_SECRET }}
+ refreshToken: ${{ secrets.G_REFRESH_TOKEN }}
+
+ - name: Upload to Google Web Store
+ id: webStoreUpload
+ continue-on-error: true
+ uses: cardinalby/webext-buildtools-chrome-webstore-upload-action@8db7a005529498d95d3e2e0166f6f4050d2b96a5
+ with:
+ zipFilePath: ${{ env.ZIP_FILE_PATH }}
+ extensionId: ${{ secrets.G_EXTENSION_ID }}
+ apiAccessToken: ${{ steps.fetchAccessToken.outputs.accessToken }}
+ waitForUploadCheckCount: 10
+ waitForUploadCheckIntervalMs: 180000 # 3 minutes
+
+ # Schedule a next attempt if store refused to accept new version because it
+ # still has a previous one in review
+ - name: Start the next attempt with the delay
+ uses: aurelien-baudet/workflow-dispatch@93e95b157d791ae7f42aef8f8a0d3d723eba1c31 # pin@v2
+ if: |
+ steps.getNextAttemptNumber.outputs.result &&
+ steps.webStoreUpload.outputs.inReviewError == 'true'
+ with:
+ workflow: ${{ github.workflow }}
+ token: ${{ secrets.WORKFLOWS_TOKEN }}
+ wait-for-completion: false
+ inputs: |
+ {
+ "attemptNumber": "${{ steps.getNextAttemptNumber.outputs.result }}",
+ "maxAttempts": "${{ github.event.inputs.maxAttempts }}",
+ "environment": "12hoursDelay"
+ }
+
+ - name: Abort on unrecoverable upload error
+ if: |
+ !steps.webStoreUpload.outputs.newVersion &&
+ steps.webStoreUpload.outputs.sameVersionAlreadyUploadedError != 'true'
+ run: exit 1
+
+ - name: Publish on Google Web Store
+ id: webStorePublish
+ if: |
+ steps.webStoreUpload.outputs.newVersion ||
+ steps.webStoreUpload.outputs.sameVersionAlreadyUploadedError == 'true'
+ uses: cardinalby/webext-buildtools-chrome-webstore-publish-action@d39ebd4ab4ea4b44498bf5fc34d4b3db7706f1ed
+ with:
+ extensionId: ${{ secrets.G_EXTENSION_ID }}
+ apiAccessToken: ${{ steps.fetchAccessToken.outputs.accessToken }}
+
+ download-published-crx:
+ needs: publish-on-webstore
+ if: needs.publish-on-webstore.outputs.result == 'success'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Download published crx file
+ id: gWebStoreDownloadCrx
+ uses: cardinalby/webext-buildtools-chrome-webstore-download-crx-action@7de7ffb52fac6255a343c5e982871eb23cbee253
+ with:
+ extensionId: ${{ secrets.G_EXTENSION_ID }}
+ crxFilePath: ${{ env.WEBSTORE_CRX_FILE_PATH }}
+
+ - name: Upload webstore published crx release asset
+ if: needs.publish-on-webstore.outputs.releaseUploadUrl
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ needs.publish-on-webstore.outputs.releaseUploadUrl }}
+ asset_path: ${{ env.WEBSTORE_CRX_FILE_PATH }}
+ asset_name: ${{ env.WEBSTORE_CRX_FILE_NAME }}
+ asset_content_type: application/x-chrome-extension
+
+ - name: Upload webstore crx file artifact to workflow
+ if: '!needs.publish-on-webstore.outputs.releaseUploadUrl'
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+ with:
+ name: ${{ env.WEBSTORE_CRX_FILE_NAME }}
+ path: ${{ env.WEBSTORE_CRX_FILE_PATH }}
diff --git a/.github/workflows/publish-on-edge-add-ons.yml b/.github/workflows/publish-on-edge-add-ons.yml
new file mode 100644
index 00000000..ddce9b18
--- /dev/null
+++ b/.github/workflows/publish-on-edge-add-ons.yml
@@ -0,0 +1,27 @@
+name: publish-on-edge-add-ons
+on:
+ workflow_dispatch:
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Obtain packed zip
+ uses: ./.github/workflows/actions/get-zip-asset
+ with:
+ githubToken: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Deploy to Edge Addons
+ uses: wdzeng/edge-addon@fe088a3bb9bf7c3f1cab08df6269664b9f7bf4fd # pin@v1.0.3
+ with:
+ product-id: ${{ secrets.EDGE_PRODUCT_ID }}
+ zip-path: ${{ env.ZIP_FILE_PATH }}
+ client-id: ${{ secrets.EDGE_CLIENT_ID }}
+ client-secret: ${{ secrets.EDGE_CLIENT_SECRET }}
+ access-token-url: ${{ secrets.EDGE_ACCESS_TOKEN_URL }}
\ No newline at end of file
diff --git a/.github/workflows/publish-release-on-tag.yml b/.github/workflows/publish-release-on-tag.yml
new file mode 100644
index 00000000..79b86e99
--- /dev/null
+++ b/.github/workflows/publish-release-on-tag.yml
@@ -0,0 +1,71 @@
+name: Release and publish on tag
+on:
+ push:
+ tags:
+ - '*.*.*'
+ workflow_dispatch:
+jobs:
+ build-release-publish:
+ if: github.ref_type == 'tag'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: cardinalby/export-env-action@66657b34899a2d695434ed060d9f2215db9b4035
+ with:
+ envFile: './.github/workflows/constants.env'
+ expand: true
+
+ - name: Look for existing release
+ id: getRelease
+ uses: cardinalby/git-get-release-action@cedef2faf69cb7c55b285bad07688d04430b7ada
+ continue-on-error: true
+ with:
+ tag: ${{ github.ref_name }}
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+ - name: Build, test and pack to zip
+ id: buildPack
+ if: steps.getRelease.outcome != 'success'
+ uses: ./.github/workflows/actions/build-test-pack
+
+ - name: Create Release
+ id: createRelease
+ if: steps.getRelease.outcome != 'success'
+ uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37 # pin@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ draft: 'true'
+
+ - name: Upload zip asset to the release
+ if: steps.getRelease.outcome != 'success'
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.createRelease.outputs.upload_url }}
+ asset_path: ${{ env.ZIP_FILE_PATH }}
+ asset_name: ${{ env.ZIP_FILE_NAME }}
+ asset_content_type: application/zip
+
+ # Should trigger build-assets-on-release.yml
+ - name: Publish release
+ if: steps.getRelease.outcome != 'success'
+ uses: eregon/publish-release@c2c0552ef2dd8209aea2a95c940a156eb8f6e9c1 # pin@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.WORKFLOWS_TOKEN }}
+ with:
+ release_id: ${{ steps.createRelease.outputs.id }}
+
+ - name: Publish on Chrome Webstore
+ uses: benc-uk/workflow-dispatch@4c044c1613fabbe5250deadc65452d54c4ad4fc7 # pin@v1
+ with:
+ workflow: publish-on-chrome-web-store
+ token: ${{ secrets.WORKFLOWS_TOKEN }}
+
+ - name: Publish on Edge Add-ons
+ uses: benc-uk/workflow-dispatch@4c044c1613fabbe5250deadc65452d54c4ad4fc7 # pin@v1
+ with:
+ workflow: publish-on-edge-add-ons
+ token: ${{ secrets.WORKFLOWS_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index bf5d07ac..00000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,236 +0,0 @@
-name: Build and Release Extension
-
-on:
- push:
- tags:
- - 'v*' # Trigger on version tags like v1.0.0
- workflow_dispatch: # Allow manual trigger
- inputs:
- version:
- description: 'Version number (e.g., 1.0.0)'
- required: true
- default: '1.0.0'
- publish_to_store:
- description: 'Publish to Chrome Web Store'
- type: boolean
- default: false
-
-env:
- EXTENSION_ID: 'benimdeioplgkhanklclahllklceahbe'
-
-jobs:
- build-and-release:
- runs-on: ubuntu-latest
-
- outputs:
- version: ${{ steps.version.outputs.version }}
- package-name: ${{ steps.package.outputs.name }}
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '18'
-
- - name: Get version
- id: version
- run: |
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- VERSION="${{ github.event.inputs.version }}"
- else
- VERSION=${GITHUB_REF#refs/tags/v}
- fi
- echo "version=${VERSION}" >> $GITHUB_OUTPUT
- echo "Version: $VERSION"
-
- - name: Update manifest version
- run: |
- VERSION="${{ steps.version.outputs.version }}"
- # Update manifest.json version
- sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" manifest.json
- echo "Updated manifest version to $VERSION"
-
- # Verify the change
- grep "version" manifest.json
-
- - name: Disable development mode in options.js
- run: |
- # Ensure production build
- sed -i 's/const DEVELOPMENT_MODE = true/const DEVELOPMENT_MODE = false/g' options/options.js
- echo "â Development mode disabled for production"
-
- - name: Create extension package
- id: package
- run: |
- VERSION="${{ steps.version.outputs.version }}"
- PACKAGE_NAME="check-extension-v${VERSION}"
-
- # Use our existing packaging script logic
- mkdir -p extension-build
-
- # Copy only necessary files for store submission
- cp manifest.json extension-build/
- cp blocked.html extension-build/
- cp -r config/ extension-build/
- cp -r images/ extension-build/
- cp -r options/ extension-build/
- cp -r popup/ extension-build/
- cp -r rules/ extension-build/
- cp -r scripts/ extension-build/
- cp -r styles/ extension-build/
-
- # Remove any development files
- find extension-build -name "*.md" -delete
- find extension-build -name "*.log" -delete
- find extension-build -name ".DS_Store" -delete
- find extension-build -name "Thumbs.db" -delete
-
- # Create the zip package
- cd extension-build
- zip -r "../${PACKAGE_NAME}.zip" .
- cd ..
-
- echo "name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
- echo "Package created: ${PACKAGE_NAME}.zip"
-
- - name: Verify package
- run: |
- PACKAGE_FILE="${{ steps.package.outputs.name }}.zip"
-
- if [ ! -f "$PACKAGE_FILE" ]; then
- echo "â Error: Package file not created"
- exit 1
- fi
-
- SIZE=$(ls -lh "$PACKAGE_FILE" | awk '{print $5}')
- echo "đĻ Package size: $SIZE"
-
- # Verify manifest is valid JSON
- unzip -p "$PACKAGE_FILE" manifest.json | jq . > /dev/null
- echo "â Manifest JSON is valid"
-
- echo "đ Package contents:"
- unzip -l "$PACKAGE_FILE"
-
- - name: Upload to Chrome Web Store
- if: ${{ github.event.inputs.publish_to_store == 'true' || github.event_name == 'push' }}
- uses: mnao305/chrome-extension-upload@v5.0.0
- with:
- file-path: ${{ steps.package.outputs.name }}.zip
- extension-id: ${{ env.EXTENSION_ID }}
- client-id: ${{ secrets.CHROME_CLIENT_ID }}
- client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
- refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
- publish: true
-
- - name: Create GitHub Release
- uses: softprops/action-gh-release@v2
- with:
- tag_name: ${{ github.ref_name || format('v{0}', steps.version.outputs.version) }}
- name: Check v${{ steps.version.outputs.version }}
- body: |
- ## đĄī¸ Check Extension v${{ steps.version.outputs.version }}
-
- **Enterprise phishing protection for Microsoft 365**
-
- ### īŋŊ Installation Options:
-
- #### Chrome Web Store (Recommended)
- - Install directly from: [Chrome Web Store](https://chrome.google.com/webstore/detail/${{ env.EXTENSION_ID }})
- - Automatic updates and security
-
- #### Manual Installation (Enterprise/Development)
- 1. Download `${{ steps.package.outputs.name }}.zip`
- 2. Extract to a folder
- 3. Open Chrome â `chrome://extensions/`
- 4. Enable "Developer mode"
- 5. Click "Load unpacked" â Select extracted folder
-
- ### đĸ Enterprise Deployment
-
- For enterprise managed deployments, use the registry files in the `enterprise/` folder:
- - `registry-chrome-store.reg` - For Chrome Web Store installations
- - `registry-edge-store.reg` - For Edge Add-ons installations
-
- Extension IDs:
- - Chrome: `${{ env.EXTENSION_ID }}`
- - Edge: (pending publication)
-
- ### ⨠Features:
- - ⥠Real-time phishing detection for Microsoft 365
- - đĸ Enterprise policy support (GPO/Intune)
- - đ¨ Customizable branding and configuration
- - đ Comprehensive logging and monitoring
- - đ Modern Manifest V3 architecture
- - đ Multi-browser support (Chrome, Edge)
-
- ### đ Requirements:
- - Chrome/Chromium browser (version 88+)
- - Manifest V3 support
-
- ### đ§ Enterprise Configuration:
- ```bash
- # Update registry with store extension ID
- .\Update-StoreIDs.ps1 -ChromeID "${{ env.EXTENSION_ID }}"
-
- # Deploy policies
- cd enterprise
- .\Deploy-ADMX.ps1
- ```
-
- ---
- **Built by CyberDrain** | Released $(date '+%Y-%m-%d %H:%M UTC')
- files: |
- ${{ steps.package.outputs.name }}.zip
- draft: false
- prerelease: ${{ contains(steps.version.outputs.version, 'beta') || contains(steps.version.outputs.version, 'alpha') }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Upload build artifact
- uses: actions/upload-artifact@v4
- with:
- name: extension-v${{ steps.version.outputs.version }}
- path: ${{ steps.package.outputs.name }}.zip
- retention-days: 90
-
- update-enterprise-configs:
- needs: build-and-release
- runs-on: ubuntu-latest
- if: ${{ github.event.inputs.publish_to_store == 'true' || github.event_name == 'push' }}
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Update enterprise registry files
- run: |
- VERSION="${{ needs.build-and-release.outputs.version }}"
-
- # Update registry files with current Chrome extension ID
- sed -i "s/CHROME_EXTENSION_ID/${{ env.EXTENSION_ID }}/g" enterprise/registry-chrome-store.reg
-
- echo "â Updated enterprise registry files with extension ID: ${{ env.EXTENSION_ID }}"
-
- # Show updated files
- echo "đ Updated registry files:"
- grep -l "${{ env.EXTENSION_ID }}" enterprise/*.reg || true
-
- - name: Commit updated enterprise configs
- run: |
- git config --local user.email "action@github.com"
- git config --local user.name "GitHub Action"
-
- if [ -n "$(git status --porcelain)" ]; then
- git add enterprise/
- git commit -m "chore: update enterprise configs for v${{ needs.build-and-release.outputs.version }}"
- git push
- echo "â Committed updated enterprise configurations"
- else
- echo "âšī¸ No changes to commit"
- fi
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/blocked.html b/blocked.html
index 5363776d..ead857c7 100644
--- a/blocked.html
+++ b/blocked.html
@@ -281,10 +281,30 @@
.btn {
width: 100%;
}
+
+ }
+
+ /* Anti-flicker: Hide content until fully loaded */
+ body.loading .container {
+ opacity: 0;
+ }
+
+ .container {
+ opacity: 1;
+ transition: opacity 0.2s ease-in;
}
+
+ .footer {
+ margin-top: 40px;
+ padding-top: 20px;
+ border-top: 1px solid #e5e7eb;
+ font-size: 12px;
+ color: #9ca3af;
+ }
+
-
+
@@ -309,6 +329,9 @@
Access Blocked
+
diff --git a/config/managed_schema.json b/config/managed_schema.json
index 96efa860..46a53e75 100644
--- a/config/managed_schema.json
+++ b/config/managed_schema.json
@@ -70,6 +70,43 @@
"type": "boolean",
"default": false
},
+ "genericWebhook": {
+ "title": "Generic Webhook",
+ "description": "Generic webhook configuration for sending detection events to custom endpoint",
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "title": "Enabled",
+ "description": "Enable generic webhook",
+ "type": "boolean",
+ "default": false
+ },
+ "url": {
+ "title": "Webhook URL",
+ "description": "The URL to send webhook payloads to",
+ "type": "string",
+ "format": "uri",
+ "default": ""
+ },
+ "events": {
+ "title": "Event Types",
+ "description": "Array of event types to send to this webhook",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "detection_alert",
+ "false_positive_report",
+ "page_blocked",
+ "rogue_app_detected",
+ "threat_detected",
+ "validation_event"
+ ]
+ },
+ "default": []
+ }
+ }
+ },
"customBranding": {
"title": "Custom Branding",
"description": "Custom branding configuration for white labeling",
diff --git a/docs/deployment/chrome-edge-deployment-instructions/macos.md b/docs/deployment/chrome-edge-deployment-instructions/macos.md
index d6165437..0b65f0cc 100644
--- a/docs/deployment/chrome-edge-deployment-instructions/macos.md
+++ b/docs/deployment/chrome-edge-deployment-instructions/macos.md
@@ -4,4 +4,4 @@ icon: apple
# MacOS
-Coming soon. If you have experience deploying managed MacOS browser extensions, please contribute to the [docs via GitHub](https://github.com/CyberDrain/Check/tree/main/docs). All Mac resources in the GitHub repo should be considered inaccurate until tested.
+Coming soon. If you have experience deploying managed MacOS browser extensions, please contribute to the [docs via GitHub](https://github.com/CyberDrain/Check/tree/dev/docs). All Mac resources in the GitHub repo should be considered inaccurate until tested.
diff --git a/docs/deployment/chrome-edge-deployment-instructions/windows/domain-deployment.md b/docs/deployment/chrome-edge-deployment-instructions/windows/domain-deployment.md
index 49d174f4..1a651c61 100644
--- a/docs/deployment/chrome-edge-deployment-instructions/windows/domain-deployment.md
+++ b/docs/deployment/chrome-edge-deployment-instructions/windows/domain-deployment.md
@@ -30,9 +30,9 @@ Documentation to follow
1. Download the following from the Check repo on GitHub
- 1. â[Deploy-ADMX.ps1](../../../../enterprise/Deploy-ADMX.ps1)
- 2. â[Check-Extension.admx](../../../../enterprise/admx/Check-Extension.admx)â
- 3. â[Check-Extension.adml](../../../../enterprise/admx/en-US/Check-Extension.adml)â
+ 1. â[Deploy-ADMX.ps1](https://github.com/CyberDrain/Check/blob/main/enterprise/Deploy-ADMX.ps1)
+ 2. â[Check-Extension.admx](https://github.com/CyberDrain/Check/blob/main/enterprise/admx/Check-Extension.admx)â
+ 3. â[Check-Extension.adml](https://github.com/CyberDrain/Check/blob/main/enterprise/admx/en-US/Check-Extension.adml)â
2. Run Deploy-ADMX.ps1. As long as you keep the other two files in the same folder, it will correctly add the available objects to Group Policy.
3. Open Group Policy and create a policy using the imported settings that can be found at `Computer Configuration â Policies â Administrative Templates â CyberDrain â Check - Microsoft 365 Phishing Protection`
diff --git a/docs/settings/branding.md b/docs/settings/branding.md
index 1b41b5b8..f635c235 100644
--- a/docs/settings/branding.md
+++ b/docs/settings/branding.md
@@ -11,7 +11,7 @@ Most individual users can skip this section unless they want to personalize the
## Company Information
{% hint style="warning" %}
-### What if Settings Are Not Visible?
+#### What if Settings Are Not Visible?
If some settings do not appear on your version, it means your organization's IT department has set these for you. This is normal in business environments - your IT team wants to make sure everyone has the same security settings. You will also see text indicating that the extension is being managed by policy.
{% endhint %}
diff --git a/docs/settings/general.md b/docs/settings/general.md
index 9a55cedd..de261b56 100644
--- a/docs/settings/general.md
+++ b/docs/settings/general.md
@@ -30,9 +30,140 @@ Currently, CIPP displays these alerts in the logbook. Future updates to CIPP are
You can monitor CIPP reporting status and activity in [Activity Logs](activity-logs.md).
{% endhint %}
+### **False Positive Webhook URL**
+
+This setting allows you to configure a webhook endpoint that receives false positive reports from users. When configured, a "Report False Positive" button will appear on blocked pages, allowing users to report when Check has incorrectly blocked a legitimate website.
+
+Enter the full URL to your webhook endpoint (e.g., `https://your-server.com/api/false-positive`). When a user clicks the "Report False Positive" button, Check will send a POST request with comprehensive detection data to help you review and improve your detection rules.
+
+#### Webhook Payload Structure
+
+Your webhook endpoint will receive a POST request with `Content-Type: application/json` containing the following fields:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `timestamp` | string | ISO 8601 timestamp when the report was submitted |
+| `reportType` | string | Always "false_positive" |
+| `blockedUrl` | string | The defanged URL that was blocked (colons replaced with `[:]`) |
+| `blockReason` | string | User-facing explanation for why the page was blocked |
+| `userAgent` | string | Complete browser user agent string |
+| `browserInfo` | object | Browser environment details (see below) |
+| `screenResolution` | object | Display information (see below) |
+| `detectionDetails` | object | Complete detection data (see below) |
+| `extensionVersion` | string | Version of Check that generated the report |
+
+**browserInfo object:**
+- `platform` - Operating system (e.g., "Linux x86_64", "Win32", "MacIntel")
+- `language` - Browser language setting (e.g., "en-US")
+- `vendor` - Browser vendor (e.g., "Google Inc.")
+- `cookiesEnabled` - Boolean indicating if cookies are enabled
+- `onLine` - Boolean indicating network connectivity status
+
+**screenResolution object:**
+- `width` - Screen width in pixels
+- `height` - Screen height in pixels
+- `availWidth` - Available screen width (excluding taskbars)
+- `availHeight` - Available screen height (excluding taskbars)
+- `colorDepth` - Color depth in bits (e.g., 24)
+
+**detectionDetails object:**
+- `url` - Original URL (non-defanged)
+- `score` - Legitimacy score assigned by detection engine
+- `threshold` - Threshold value that triggered the block
+- `reason` - Detailed technical reason for blocking
+- `pageTitle` - Title of the blocked page
+- `timestamp` - When the page was blocked
+- `threats` - Array of threat objects with `id`, `type`, `description`, and `severity`
+- `phishingIndicators` - Array of specific indicators that triggered detection
+- Additional fields depending on detection method used
+
+#### Complete Payload Example
+
+```json
+{
+ "timestamp": "2025-11-05T21:30:00.000Z",
+ "reportType": "false_positive",
+ "blockedUrl": "https[:]//example[.]com/login",
+ "blockReason": "This website looks like it has tried to steal your login credentials, to prevent you from logging in we've blocked access.",
+ "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
+ "browserInfo": {
+ "platform": "Linux x86_64",
+ "language": "en-US",
+ "vendor": "Google Inc.",
+ "cookiesEnabled": true,
+ "onLine": true
+ },
+ "screenResolution": {
+ "width": 1920,
+ "height": 1080,
+ "availWidth": 1920,
+ "availHeight": 1040,
+ "colorDepth": 24
+ },
+ "detectionDetails": {
+ "url": "https://example.com/login",
+ "score": 42,
+ "threshold": 50,
+ "reason": "Multiple phishing indicators detected: score 42/50 (3 phishing indicators)",
+ "pageTitle": "Sign In - Example Services",
+ "timestamp": "2025-11-05T21:29:45.000Z",
+ "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
+ "threats": [
+ {
+ "id": "phi_suspicious_domain",
+ "type": "domain_analysis",
+ "description": "Domain closely resembles microsoft.com",
+ "severity": "high"
+ },
+ {
+ "id": "phi_fake_login_form",
+ "type": "form_analysis",
+ "description": "Login form mimics Microsoft 365 sign-in",
+ "severity": "medium"
+ }
+ ],
+ "phishingIndicators": [
+ {
+ "id": "phi_suspicious_domain",
+ "description": "Domain closely resembles microsoft.com",
+ "severity": "high"
+ },
+ {
+ "id": "phi_fake_login_form",
+ "description": "Login form mimics Microsoft 365 sign-in",
+ "severity": "medium"
+ },
+ {
+ "id": "phi_suspicious_title",
+ "description": "Page title suggests Microsoft login",
+ "severity": "low"
+ }
+ ]
+ },
+ "extensionVersion": "1.0.0"
+}
+```
+
+#### Webhook Requirements
+
+Your webhook endpoint should:
+1. Accept POST requests with `Content-Type: application/json`
+2. Respond with HTTP status codes:
+ - `200 OK` - Report successfully received
+ - `4xx` - Client error (user will see error message)
+ - `5xx` - Server error (user will see error message)
+3. Respond within 30 seconds to avoid timeout
+4. Use HTTPS to protect sensitive detection data in transit
+
+{% hint style="info" %}
+**Usage Notes:**
+- Leave this field empty if you don't want to enable false positive reporting
+- The "Report False Positive" button only appears when this webhook URL is configured
+{% endhint %}
+
## User Interface
-### **Show Notifications**
+### **Show Notifications**
When Check blocks a dangerous website or finds something suspicious, it can show you a small popup message to let you know what's going on. We recommend leaving this setting enabled
@@ -41,8 +172,7 @@ When Check blocks a dangerous website or finds something suspicious, it can show
This adds a small green checkmark to real Microsoft login pages. This feature is optional.
{% hint style="warning" %}
-
-### What if Settings Are Not Visible?
+#### What if Settings Are Not Visible?
If some settings do not appear on my version, it means your organization's IT department has set these for you. This is normal in business environments - your IT team wants to make sure everyone has the same security settings. You will also see text indicating that the extension is being managed by policy.
{% endhint %}
diff --git a/docs/webhooks.md b/docs/webhooks.md
new file mode 100644
index 00000000..fc11d03a
--- /dev/null
+++ b/docs/webhooks.md
@@ -0,0 +1,268 @@
+# Webhook System
+
+## Configuration
+
+Configure a single generic webhook that can receive multiple event types:
+
+```json
+{
+ "genericWebhook": {
+ "enabled": true,
+ "url": "https://webhook.example.com/endpoint",
+ "events": [
+ "detection_alert",
+ "false_positive_report",
+ "page_blocked",
+ "rogue_app_detected"
+ ]
+ }
+}
+```
+
+CIPP reporting uses separate dedicated settings:
+
+```json
+{
+ "enableCippReporting": true,
+ "cippServerUrl": "https://cipp-server.com",
+ "cippTenantId": "tenant-id"
+}
+```
+
+## Unified Webhook Schema
+
+All webhook payloads follow a consistent structure:
+
+```json
+{
+ "version": "1.0",
+ "type": "",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "user": { /* optional user profile */ },
+ "browser": { /* optional browser context */ },
+ "tenantId": "tenant-id",
+ "data": {
+ "url": "https://example.com",
+ "severity": "high|medium|low|critical|info",
+ "score": 0,
+ "threshold": 85,
+ "reason": "Description of event",
+ "detectionMethod": "rules_engine|rogue_app_detection|etc",
+ "rule": "rule-id",
+ "ruleDescription": "Rule description",
+ "category": "phishing|oauth_threat|validation|etc",
+ "context": {
+ "referrer": null,
+ "pageTitle": null,
+ "domain": null,
+ "redirectTo": null
+ }
+ }
+}
+```
+
+## Webhook Types
+
+### detection_alert
+General phishing detection events.
+
+```json
+{
+ "version": "1.0",
+ "type": "detection_alert",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://phishing-site.example.com",
+ "severity": "high",
+ "score": 15,
+ "threshold": 85,
+ "reason": "Multiple phishing indicators detected",
+ "detectionMethod": "rules_engine",
+ "rule": "rule-1",
+ "ruleDescription": "Form posts to non-Microsoft domain",
+ "category": "phishing",
+ "confidence": 0.9,
+ "matchedRules": ["rule-1", "rule-2"],
+ "context": {
+ "referrer": "https://email-client.com",
+ "pageTitle": "Microsoft Login",
+ "domain": "phishing-site.example.com",
+ "redirectTo": null
+ }
+ }
+}
+```
+
+### false_positive_report
+User-submitted false positive reports from blocked pages.
+
+```json
+{
+ "version": "1.0",
+ "type": "false_positive_report",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://legitimate-site.example.com",
+ "severity": "info",
+ "reason": "User reported false positive",
+ "reportTimestamp": "2025-11-05T22:00:00.000Z",
+ "userAgent": "Mozilla/5.0...",
+ "browserInfo": {
+ "platform": "Linux x86_64",
+ "language": "en-US"
+ },
+ "detectionDetails": {},
+ "userComments": null,
+ "context": {
+ "referrer": null,
+ "pageTitle": null,
+ "domain": null
+ }
+ }
+}
+```
+
+### page_blocked
+Sent when a page is blocked.
+
+```json
+{
+ "version": "1.0",
+ "type": "page_blocked",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://malicious-site.example.com",
+ "severity": "critical",
+ "score": 0,
+ "threshold": 85,
+ "reason": "Phishing attempt detected",
+ "detectionMethod": "rules_engine",
+ "rule": "critical-rule-id",
+ "ruleDescription": "Critical phishing indicator detected",
+ "category": "phishing",
+ "action": "blocked",
+ "context": {
+ "referrer": null,
+ "pageTitle": "Fake Login",
+ "domain": "malicious-site.example.com",
+ "redirectTo": null
+ }
+ }
+}
+```
+
+### rogue_app_detected
+OAuth rogue application detection events.
+
+```json
+{
+ "version": "1.0",
+ "type": "rogue_app_detected",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://login.microsoftonline.com/...",
+ "severity": "critical",
+ "reason": "Rogue OAuth application detected",
+ "detectionMethod": "rogue_app_detection",
+ "category": "oauth_threat",
+ "clientId": "app-client-id",
+ "appName": "Suspicious App",
+ "appInfo": {
+ "description": "Known malicious OAuth application",
+ "tags": ["BEC", "exfiltration"],
+ "references": ["https://..."],
+ "risk": "high"
+ },
+ "context": {
+ "referrer": null,
+ "pageTitle": null,
+ "domain": null,
+ "redirectTo": "https://malicious-redirect.com",
+ "isLocalhost": false,
+ "isPrivateIP": false
+ }
+ }
+}
+```
+
+### threat_detected
+General threat detection events.
+
+```json
+{
+ "version": "1.0",
+ "type": "threat_detected",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://suspicious-site.example.com",
+ "severity": "medium",
+ "score": 50,
+ "threshold": 85,
+ "reason": "Suspicious content detected",
+ "detectionMethod": "content_analysis",
+ "rule": null,
+ "category": "credential_harvesting",
+ "confidence": 0.75,
+ "indicators": ["fake-login-form", "typosquatting"],
+ "matchedRules": ["rule-a", "rule-b"],
+ "context": {
+ "referrer": null,
+ "pageTitle": "Login",
+ "domain": "suspicious-site.example.com",
+ "redirectTo": null
+ }
+ }
+}
+```
+
+### validation_event
+Legitimate page validation events.
+
+```json
+{
+ "version": "1.0",
+ "type": "validation_event",
+ "timestamp": "2025-11-05T22:00:00.000Z",
+ "source": "Check Extension",
+ "extensionVersion": "1.0.0",
+ "data": {
+ "url": "https://login.microsoftonline.com",
+ "severity": "info",
+ "reason": "Legitimate domain validated",
+ "detectionMethod": "domain_validation",
+ "category": "validation",
+ "result": "legitimate",
+ "confidence": 1.0,
+ "context": {
+ "referrer": null,
+ "pageTitle": null,
+ "domain": "login.microsoftonline.com",
+ "redirectTo": null
+ }
+ }
+}
+```
+
+## HTTP Headers
+
+All webhook requests include:
+
+```
+Content-Type: application/json
+User-Agent: Check/{version}
+X-Webhook-Type: {webhook-type}
+X-Webhook-Version: 1.0
+```
+
diff --git a/enterprise/Remove-Windows-Chrome-and-Edge.ps1 b/enterprise/Remove-Windows-Chrome-and-Edge.ps1
new file mode 100644
index 00000000..fed3c9d9
--- /dev/null
+++ b/enterprise/Remove-Windows-Chrome-and-Edge.ps1
@@ -0,0 +1,139 @@
+# Define extension details (same as install-check.ps1)
+# Chrome
+$chromeExtensionId = "benimdeioplgkhanklclahllklceahbe"
+$chromeManagedStorageKey = "HKLM:\SOFTWARE\Policies\Google\Chrome\3rdparty\extensions\$chromeExtensionId\policy"
+$chromeExtensionSettingsKey = "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionSettings\$chromeExtensionId"
+
+# Edge
+$edgeExtensionId = "knepjpocdagponkonnbggpcnhnaikajg"
+$edgeManagedStorageKey = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\3rdparty\extensions\$edgeExtensionId\policy"
+$edgeExtensionSettingsKey = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionSettings\$edgeExtensionId"
+
+# Function to remove extension settings
+function Remove-ExtensionSettings {
+ param (
+ [string]$ExtensionId,
+ [string]$ManagedStorageKey,
+ [string]$ExtensionSettingsKey
+ )
+
+ # Remove properties from managed storage key
+ if (Test-Path $ManagedStorageKey) {
+ $propertiesToRemove = @(
+ "showNotifications",
+ "enableValidPageBadge",
+ "enablePageBlocking",
+ "enableCippReporting",
+ "cippServerUrl",
+ "cippTenantId",
+ "customRulesUrl",
+ "updateInterval",
+ "enableDebugLogging"
+ )
+
+ foreach ($property in $propertiesToRemove) {
+ if (Get-ItemProperty -Path $ManagedStorageKey -Name $property -ErrorAction SilentlyContinue) {
+ Remove-ItemProperty -Path $ManagedStorageKey -Name $property -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed property: $property from $ManagedStorageKey"
+ }
+ }
+
+ # Remove URL allowlist subkey and all its properties
+ $urlAllowlistKey = "$ManagedStorageKey\urlAllowlist"
+ if (Test-Path $urlAllowlistKey) {
+ # Remove all numbered properties (1, 2, 3, etc.)
+ $properties = Get-ItemProperty -Path $urlAllowlistKey -ErrorAction SilentlyContinue
+ if ($properties) {
+ $properties.PSObject.Properties | Where-Object { $_.Name -match '^\d+$' } | ForEach-Object {
+ Remove-ItemProperty -Path $urlAllowlistKey -Name $_.Name -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed URL allowlist property: $($_.Name) from $urlAllowlistKey"
+ }
+ }
+ # Remove the urlAllowlist subkey if it's empty
+ try {
+ Remove-Item -Path $urlAllowlistKey -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed URL allowlist subkey: $urlAllowlistKey"
+ } catch {
+ # Key may not be empty or may have been removed already
+ }
+ }
+
+ # Remove custom branding subkey and all its properties
+ $customBrandingKey = "$ManagedStorageKey\customBranding"
+ if (Test-Path $customBrandingKey) {
+ $brandingPropertiesToRemove = @(
+ "companyName",
+ "companyURL",
+ "productName",
+ "supportEmail",
+ "primaryColor",
+ "logoUrl"
+ )
+
+ foreach ($property in $brandingPropertiesToRemove) {
+ if (Get-ItemProperty -Path $customBrandingKey -Name $property -ErrorAction SilentlyContinue) {
+ Remove-ItemProperty -Path $customBrandingKey -Name $property -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed custom branding property: $property from $customBrandingKey"
+ }
+ }
+
+ # Remove the customBranding subkey if it's empty
+ try {
+ Remove-Item -Path $customBrandingKey -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed custom branding subkey: $customBrandingKey"
+ } catch {
+ # Key may not be empty or may have been removed already
+ }
+ }
+
+ # Remove the managed storage key if it's empty
+ try {
+ $remainingProperties = Get-ItemProperty -Path $ManagedStorageKey -ErrorAction SilentlyContinue
+ if ($remainingProperties -and $remainingProperties.PSObject.Properties.Count -eq 0) {
+ Remove-Item -Path $ManagedStorageKey -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed managed storage key: $ManagedStorageKey"
+ }
+ } catch {
+ # Key may not be empty or may have been removed already
+ }
+ }
+
+ # Remove properties from extension settings key
+ if (Test-Path $ExtensionSettingsKey) {
+ $extensionPropertiesToRemove = @(
+ "installation_mode",
+ "update_url"
+ )
+
+ # Add browser-specific toolbar properties
+ if ($ExtensionId -eq $edgeExtensionId) {
+ $extensionPropertiesToRemove += "toolbar_state"
+ } elseif ($ExtensionId -eq $chromeExtensionId) {
+ $extensionPropertiesToRemove += "toolbar_pin"
+ }
+
+ foreach ($property in $extensionPropertiesToRemove) {
+ if (Get-ItemProperty -Path $ExtensionSettingsKey -Name $property -ErrorAction SilentlyContinue) {
+ Remove-ItemProperty -Path $ExtensionSettingsKey -Name $property -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed extension setting property: $property from $ExtensionSettingsKey"
+ }
+ }
+
+ # Remove the extension settings key if it's empty
+ try {
+ $remainingProperties = Get-ItemProperty -Path $ExtensionSettingsKey -ErrorAction SilentlyContinue
+ if ($remainingProperties -and $remainingProperties.PSObject.Properties.Count -eq 0) {
+ Remove-Item -Path $ExtensionSettingsKey -Force -ErrorAction SilentlyContinue
+ Write-Host "Removed extension settings key: $ExtensionSettingsKey"
+ }
+ } catch {
+ # Key may not be empty or may have been removed already
+ }
+ }
+
+ Write-Host "Completed removal of extension settings for $ExtensionId"
+}
+
+# Remove settings for Chrome and Edge
+Remove-ExtensionSettings -ExtensionId $chromeExtensionId -ManagedStorageKey $chromeManagedStorageKey -ExtensionSettingsKey $chromeExtensionSettingsKey
+Remove-ExtensionSettings -ExtensionId $edgeExtensionId -ManagedStorageKey $edgeManagedStorageKey -ExtensionSettingsKey $edgeExtensionSettingsKey
diff --git a/enterprise/Test-Extension-Policy.ps1 b/enterprise/Test-Extension-Policy.ps1
new file mode 100644
index 00000000..6a07d23e
--- /dev/null
+++ b/enterprise/Test-Extension-Policy.ps1
@@ -0,0 +1,121 @@
+# Quick Test Script for Check Extension Registry Settings
+# Run as Administrator: Right-click PowerShell -> Run as Administrator
+# Then execute: .\Test-Extension-Policy.ps1
+
+# Extension IDs
+$chromeExtId = "jlpkafnpidpjinmghilbonlgnilmkknn"
+$edgeExtId = "jlpkafnpidpjinmghilbonlgnilmkknn"
+
+# Registry paths
+$chromePolicyKey = "HKLM:\SOFTWARE\Policies\Google\Chrome\3rdparty\extensions\$chromeExtId\policy"
+$edgePolicyKey = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\3rdparty\extensions\$edgeExtId\policy"
+
+# Test configuration (modify these values to test different settings)
+$testConfig = @{
+ showNotifications = 1
+ enableValidPageBadge = 1
+ enablePageBlocking = 0
+ enableCippReporting = 0
+ cippServerUrl = ""
+ cippTenantId = ""
+ customRulesUrl = ""
+ updateInterval = 24
+ enableDebugLogging = 1
+}
+
+# Custom branding test values
+$testBranding = @{
+ companyName = "Test Company"
+ companyURL = "https://example.com"
+ productName = "Test Product"
+ supportEmail = "test@example.com"
+ primaryColor = "#FF6B00"
+ logoUrl = ""
+}
+
+function Set-TestPolicies {
+ param([string]$PolicyKey)
+
+ if (!(Test-Path $PolicyKey)) {
+ New-Item -Path $PolicyKey -Force | Out-Null
+ Write-Output "Created policy key: $PolicyKey"
+ }
+
+ foreach ($key in $testConfig.Keys) {
+ $value = $testConfig[$key]
+ $type = if ($value -is [int]) { "DWord" } else { "String" }
+ New-ItemProperty -Path $PolicyKey -Name $key -PropertyType $type -Value $value -Force | Out-Null
+ }
+
+ $brandingKey = "$PolicyKey\customBranding"
+ if (!(Test-Path $brandingKey)) {
+ New-Item -Path $brandingKey -Force | Out-Null
+ }
+
+ foreach ($key in $testBranding.Keys) {
+ New-ItemProperty -Path $brandingKey -Name $key -PropertyType String -Value $testBranding[$key] -Force | Out-Null
+ }
+
+ Write-Output "Applied test policies to: $PolicyKey"
+}
+
+function Show-CurrentPolicies {
+ param([string]$PolicyKey)
+
+ if (Test-Path $PolicyKey) {
+ Write-Output "`nCurrent policies in $PolicyKey"
+ Get-ItemProperty -Path $PolicyKey | Format-List
+
+ $brandingKey = "$PolicyKey\customBranding"
+ if (Test-Path $brandingKey) {
+ Write-Output "`nCustom Branding:"
+ Get-ItemProperty -Path $brandingKey | Format-List
+ }
+ } else {
+ Write-Output "No policies set at: $PolicyKey"
+ }
+}
+
+function Remove-TestPolicies {
+ param([string]$PolicyKey)
+
+ if (Test-Path $PolicyKey) {
+ Remove-Item -Path $PolicyKey -Recurse -Force
+ Write-Output "Removed test policies from: $PolicyKey"
+ }
+}
+
+Write-Output "=== Check Extension Policy Testing Tool ==="
+Write-Output ""
+Write-Output "1. Apply test policies (Chrome & Edge)"
+Write-Output "2. Show current policies"
+Write-Output "3. Remove test policies"
+Write-Output "4. Exit"
+Write-Output ""
+$choice = Read-Host "Select option"
+
+switch ($choice) {
+ "1" {
+ Write-Output "`nApplying test policies..."
+ Set-TestPolicies -PolicyKey $chromePolicyKey
+ Set-TestPolicies -PolicyKey $edgePolicyKey
+ Write-Output "`nDone! Restart Chrome/Edge to apply changes."
+ Write-Output "View policies at: chrome://policy or edge://policy"
+ }
+ "2" {
+ Show-CurrentPolicies -PolicyKey $chromePolicyKey
+ Show-CurrentPolicies -PolicyKey $edgePolicyKey
+ }
+ "3" {
+ Write-Output "`nRemoving test policies..."
+ Remove-TestPolicies -PolicyKey $chromePolicyKey
+ Remove-TestPolicies -PolicyKey $edgePolicyKey
+ Write-Output "`nDone! Restart Chrome/Edge to clear changes."
+ }
+ "4" {
+ exit
+ }
+ default {
+ Write-Output "Invalid option"
+ }
+}
diff --git a/enterprise/admx/en-US/Check-Extension.adml b/enterprise/admx/en-US/Check-Extension.adml
index 3b39a3dc..65114ba4 100644
--- a/enterprise/admx/en-US/Check-Extension.adml
+++ b/enterprise/admx/en-US/Check-Extension.adml
@@ -1,399 +1,466 @@
-
-
-
-
-
-
- CyberDrain
- Check - Phishing Protection
- Microsoft Edge
- Google Chrome
-
-
- Configure Check extension installation (Edge)
- This policy configures the installation mode for the Check extension in Microsoft Edge, ensuring it is force-installed and pinned to the toolbar.
-
-The extension will be:
-- Force installed (cannot be removed by users)
-- Automatically updated from the Edge Add-ons store
-- Pinned to the browser toolbar
-
-Extension ID: knepjpocdagponkonnbggpcnhnaikajg
-
-
- Configure Check extension installation (Chrome)
- This policy configures the installation mode for the Check extension in Google Chrome, ensuring it is force-installed and pinned to the toolbar.
-
-The extension will be:
-- Force installed (cannot be removed by users)
-- Automatically updated from the Chrome Web Store
-- Pinned to the browser toolbar
-
-Extension ID: benimdeioplgkhanklclahllklceahbe
-
-
- Display security notifications
- This policy controls whether the Check extension displays security notifications and warnings to users.
-
-When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
-When disabled: The extension operates silently without displaying notifications.
-
-
- Display security notifications (Edge)
- This policy controls whether the Check extension displays security notifications and warnings to users in Microsoft Edge.
-
-When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
-When disabled: The extension operates silently without displaying notifications.
-
-
- Display security notifications (Chrome)
- This policy controls whether the Check extension displays security notifications and warnings to users in Google Chrome.
-
-When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
-When disabled: The extension operates silently without displaying notifications.
-
-
- Show valid page badge
- This policy controls whether the Check extension displays a validation badge on legitimate Microsoft login pages.
-
-When enabled (default): A green badge or indicator shows when users are on a verified Microsoft login page.
-When disabled: No visual indicator is shown for valid pages.
-
-
- Enable page blocking
- This policy controls whether the Check extension blocks access to detected phishing pages.
-
-When enabled (default): Suspicious and phishing pages are blocked with a warning screen.
-When disabled: Pages are only flagged but not blocked (warning mode only).
-
-
- Enable CIPP reporting
- This policy controls whether the Check extension reports security events to a CIPP (CyberDrain Improved Partner Portal) server.
-
-When enabled: Security events are sent to the configured CIPP server for centralized monitoring.
-When disabled (default): No reporting to external servers occurs.
-
-Note: Requires CIPP Server URL to be configured.
-
-
- CIPP server URL
- This policy specifies the base URL for the CIPP server where security events should be reported.
-
-Example: https://cipp.yourcompany.com
-
-This setting is only used when CIPP reporting is enabled. The URL should point to a valid CIPP server instance.
-
-
- CIPP tenant identifier
- This policy specifies the tenant identifier to include with CIPP alerts for multi-tenant environments.
-
-Example: contoso.onmicrosoft.com
-
-This helps identify which tenant/organization the security event originated from when using a shared CIPP instance.
-
-
- Custom detection rules URL
- This policy specifies a custom URL from which the extension should fetch detection rules.
-
-Example: https://yourcompany.com/detection-rules.json
-
-When specified, the extension will download detection rules from this URL instead of using the default rules. Leave empty to use default detection rules.
-
-The URL must serve a valid JSON file matching the detection rules schema.
-
-
- Detection rules update interval
- This policy specifies how often (in hours) the extension should check for updates to detection rules.
-
-Default: 24 hours
-Range: 1-168 hours (1 hour to 1 week)
-
-More frequent updates provide better protection but may increase network usage.
-
-
- URL allowlist (URLs with wildcards or regex patterns)
- This policy specifies a list of URL patterns or regex patterns to allowlist URLs from detection.
-
-These patterns will be added to the exclusion rules without replacing the entire ruleset. You can use simple URLs with wildcards or advanced regex patterns.
-
-Simple URL examples with wildcards:
-- https://google.com/*
-- https://*.microsoft.com/*
-- https://login.microsoftonline.com/*
-
-Advanced regex examples:
-- ^https://trusted\.example\.com/.*
-- ^https://.*\.microsoft\.com/.*
-
-URLs matching any of these patterns will bypass phishing detection.
-
-
- URL allowlist (URLs with wildcards or regex patterns) (Chrome)
- This policy specifies a list of URL patterns or regex patterns to allowlist URLs from detection in Google Chrome.
-
-These patterns will be added to the exclusion rules without replacing the entire ruleset. You can use simple URLs with wildcards or advanced regex patterns.
-
-Simple URL examples with wildcards:
-- https://google.com/*
-- https://*.microsoft.com/*
-- https://login.microsoftonline.com/*
-
-Advanced regex examples:
-- ^https://trusted\.example\.com/.*
-- ^https://.*\.microsoft\.com/.*
-
-URLs matching any of these patterns will bypass phishing detection.
-
-
- Company name
- This policy specifies the company name to display in the extension's user interface for branding purposes.
-
-Example: Contoso Corporation
-
-The company name appears in the extension popup and settings pages.
-
-
- Product name
- This policy specifies a custom product name for the extension.
-
-Example: Contoso Security Guard
-
-This allows organizations to rebrand the extension with their own product name.
-
-
- Support email address
- This policy specifies the email address users should contact for support with the extension.
-
-Example: security@contoso.com
-
-This email address is displayed in the extension interface and help documentation.
-
-
- Primary theme color
- This policy specifies the primary theme color for the extension interface using a hex color code.
-
-Example: #0078D4
-
-The color should be in hex format (e.g., #FF0000 for red, #0078D4 for Microsoft blue). This color is used for UI elements and branding throughout the extension.
-
-
- Company logo URL
- This policy specifies the URL to a company logo that will be displayed in the extension interface.
-
-Example: https://yourcompany.com/logo.png
-
-The logo should be a square image (recommended size: 48x48 to 128x128 pixels) in PNG, SVG, or other web-compatible format. The logo will be displayed in the extension popup and blocked page.
-
-
- Enable debug logging
- This policy controls whether the Check extension generates detailed debug logs for troubleshooting purposes.
-
-When enabled: Verbose logging is activated and can be viewed in browser developer tools.
-When disabled (default): Only essential logging occurs.
-
-Debug logging should only be enabled for troubleshooting as it may impact performance and generate large log files.
-
-
- Enable debug logging (Chrome)
- This policy controls whether the Check extension generates detailed debug logs for troubleshooting purposes in Google Chrome.
-
-When enabled: Verbose logging is activated and can be viewed in browser developer tools.
-When disabled (default): Only essential logging occurs.
-
-Debug logging should only be enabled for troubleshooting as it may impact performance and generate large log files.
-
-
- Show valid page badge (Chrome)
- This policy controls whether the Check extension displays a validation badge on legitimate Microsoft login pages in Google Chrome.
-
-When enabled (default): A green badge or indicator shows when users are on a verified Microsoft login page.
-When disabled: No visual indicator is shown for valid pages.
-
-
- Enable page blocking (Chrome)
- This policy controls whether the Check extension blocks access to detected phishing pages in Google Chrome.
-
-When enabled (default): Suspicious and phishing pages are blocked with a warning screen.
-When disabled: Pages are only flagged but not blocked (warning mode only).
-
-
- Enable CIPP reporting (Chrome)
- This policy controls whether the Check extension reports security events to a CIPP (CyberDrain Improved Partner Portal) server in Google Chrome.
-
-When enabled: Security events are sent to the configured CIPP server for centralized monitoring.
-When disabled (default): No reporting to external servers occurs.
-
-Note: Requires CIPP Server URL to be configured.
-
-
- CIPP server URL (Chrome)
- This policy specifies the base URL for the CIPP server where security events should be reported from Google Chrome.
-
-Example: https://cipp.yourcompany.com
-
-This setting is only used when CIPP reporting is enabled. The URL should point to a valid CIPP server instance.
-
-
- CIPP tenant identifier (Chrome)
- This policy specifies the tenant identifier to include with CIPP alerts for multi-tenant environments from Google Chrome.
-
-Example: contoso.onmicrosoft.com
-
-This helps identify which tenant/organization the security event originated from when using a shared CIPP instance.
-
-
- Custom detection rules URL (Chrome)
- This policy specifies a custom URL from which the extension should fetch detection rules in Google Chrome.
-
-Example: https://yourcompany.com/detection-rules.json
-
-When specified, the extension will download detection rules from this URL instead of using the default rules. Leave empty to use default detection rules.
-
-The URL must serve a valid JSON file matching the detection rules schema.
-
-
- Detection rules update interval (Chrome)
- This policy specifies how often (in hours) the extension should check for updates to detection rules in Google Chrome.
-
-Default: 24 hours
-Range: 1-168 hours (1 hour to 1 week)
-
-More frequent updates provide better protection but may increase network usage.
-
-
- Company name (Chrome)
- This policy specifies the company name to display in the extension's user interface for branding purposes in Google Chrome.
-
-Example: Contoso Corporation
-
-The company name appears in the extension popup and settings pages.
-
-
- Product name (Chrome)
- This policy specifies a custom product name for the extension in Google Chrome.
-
-Example: Contoso Security Guard
-
-This allows organizations to rebrand the extension with their own product name.
-
-
- Support email address (Chrome)
- This policy specifies the email address users should contact for support with the extension in Google Chrome.
-
-Example: security@contoso.com
-
-This email address is displayed in the extension interface and help documentation.
-
-
- Primary theme color (Chrome)
- This policy specifies the primary theme color for the extension interface using a hex color code in Google Chrome.
-
-Example: #0078D4
-
-The color should be in hex format (e.g., #FF0000 for red, #0078D4 for Microsoft blue). This color is used for UI elements and branding throughout the extension.
-
-
- Company logo URL (Chrome)
- This policy specifies the URL to a company logo that will be displayed in the extension interface in Google Chrome.
-
-Example: https://yourcompany.com/logo.png
-
-The logo should be a square image (recommended size: 48x48 to 128x128 pixels) in PNG, SVG, or other web-compatible format. The logo will be displayed in the extension popup and blocked page.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update Interval (hours):
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update Interval (hours):
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- URL Allowlist (URLs with wildcards or regex patterns):
-
-
- URL Allowlist (URLs with wildcards or regex patterns):
-
-
-
-
+
+
+
+
+
+
+ CyberDrain
+ Check - Phishing Protection
+ Microsoft Edge
+ Google Chrome
+
+ Configure Check extension installation (Edge)
+
+ This policy configures the installation mode for the Check extension in Microsoft Edge, ensuring it is force-installed and pinned to the toolbar.
+
+ The extension will be:
+ - Force installed (cannot be removed by users)
+ - Automatically updated from the Edge Add-ons store
+ - Pinned to the browser toolbar
+
+ Extension ID: knepjpocdagponkonnbggpcnhnaikajg
+
+
+ Configure Check extension installation (Chrome)
+
+ This policy configures the installation mode for the Check extension in Google Chrome, ensuring it is force-installed and pinned to the toolbar.
+
+ The extension will be:
+ - Force installed (cannot be removed by users)
+ - Automatically updated from the Chrome Web Store
+ - Pinned to the browser toolbar
+
+ Extension ID: benimdeioplgkhanklclahllklceahbe
+
+
+ Display security notifications
+
+ This policy controls whether the Check extension displays security notifications and warnings to users.
+
+ When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
+ When disabled: The extension operates silently without displaying notifications.
+
+
+ Display security notifications (Edge)
+
+ This policy controls whether the Check extension displays security notifications and warnings to users in Microsoft Edge.
+
+ When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
+ When disabled: The extension operates silently without displaying notifications.
+
+
+ Display security notifications (Chrome)
+
+ This policy controls whether the Check extension displays security notifications and warnings to users in Google Chrome.
+
+ When enabled (default): Users will see notifications when phishing attempts are detected or blocked.
+ When disabled: The extension operates silently without displaying notifications.
+
+
+ Show valid page badge
+
+ This policy controls whether the Check extension displays a validation badge on legitimate Microsoft login pages.
+
+ When enabled (default): A green badge or indicator shows when users are on a verified Microsoft login page.
+ When disabled: No visual indicator is shown for valid pages.
+
+
+ Enable page blocking
+
+ This policy controls whether the Check extension blocks access to detected phishing pages.
+
+ When enabled (default): Suspicious and phishing pages are blocked with a warning screen.
+ When disabled: Pages are only flagged but not blocked (warning mode only).
+
+
+ Enable CIPP reporting
+
+ This policy controls whether the Check extension reports security events to a CIPP (CyberDrain Improved Partner Portal) server.
+
+ When enabled: Security events are sent to the configured CIPP server for centralized monitoring.
+ When disabled (default): No reporting to external servers occurs.
+
+ Note: Requires CIPP Server URL to be configured.
+
+
+ CIPP server URL
+
+ This policy specifies the base URL for the CIPP server where security events should be reported.
+
+ Example: https://cipp.yourcompany.com
+
+ This setting is only used when CIPP reporting is enabled. The URL should point to a valid CIPP server instance.
+
+
+ CIPP tenant identifier
+
+ This policy specifies the tenant identifier to include with CIPP alerts for multi-tenant environments.
+
+ Example: contoso.onmicrosoft.com
+
+ This helps identify which tenant/organization the security event originated from when using a shared CIPP instance.
+
+
+ Custom detection rules URL
+
+ This policy specifies a custom URL from which the extension should fetch detection rules.
+
+ Example: https://yourcompany.com/detection-rules.json
+
+ When specified, the extension will download detection rules from this URL instead of using the default rules. Leave empty to use default detection rules.
+
+ The URL must serve a valid JSON file matching the detection rules schema.
+
+
+ Detection rules update interval
+
+ This policy specifies how often (in hours) the extension should check for updates to detection rules.
+
+ Default: 24 hours
+ Range: 1-168 hours (1 hour to 1 week)
+
+ More frequent updates provide better protection but may increase network usage.
+
+
+ URL allowlist (URLs with wildcards or regex patterns)
+
+ This policy specifies a list of URL patterns or regex patterns to allowlist URLs from detection.
+
+ These patterns will be added to the exclusion rules without replacing the entire ruleset. You can use simple URLs with wildcards or advanced regex patterns.
+
+ Simple URL examples with wildcards:
+ - https://google.com/*
+ - https://*.microsoft.com/*
+ - https://login.microsoftonline.com/*
+
+ Advanced regex examples:
+ - ^https://trusted\.example\.com/.*
+ - ^https://.*\.microsoft\.com/.*
+
+ URLs matching any of these patterns will bypass phishing detection.
+
+
+ URL allowlist (URLs with wildcards or regex patterns) (Chrome)
+
+ This policy specifies a list of URL patterns or regex patterns to allowlist URLs from detection in Google Chrome.
+
+ These patterns will be added to the exclusion rules without replacing the entire ruleset. You can use simple URLs with wildcards or advanced regex patterns.
+
+ Simple URL examples with wildcards:
+ - https://google.com/*
+ - https://*.microsoft.com/*
+ - https://login.microsoftonline.com/*
+
+ Advanced regex examples:
+ - ^https://trusted\.example\.com/.*
+ - ^https://.*\.microsoft\.com/.*
+
+ URLs matching any of these patterns will bypass phishing detection.
+
+
+ Company name
+
+ This policy specifies the company name to display in the extension's user interface for branding purposes.
+
+ Example: Contoso Corporation
+
+ The company name appears in the extension popup and settings pages.
+
+
+ Company URL
+
+ This policy specifies the company URL used in the extension for branding and navigation purposes.
+
+ Example: https://contoso.com
+
+ The company URL is used for linking back to the company website from the extension interface.
+
+
+ Product name
+
+ This policy specifies a custom product name for the extension.
+
+ Example: Contoso Security Guard
+
+ This allows organizations to rebrand the extension with their own product name.
+
+
+ Support email address
+
+ This policy specifies the email address users should contact for support with the extension.
+
+ Example: security@contoso.com
+
+ This email address is displayed in the extension interface and help documentation.
+
+
+ Primary theme color
+
+ This policy specifies the primary theme color for the extension interface using a hex color code.
+
+ Example: #0078D4
+
+ The color should be in hex format (e.g., #FF0000 for red, #0078D4 for Microsoft blue). This color is used for UI elements and branding throughout the extension.
+
+
+ Company logo URL
+
+ This policy specifies the URL to a company logo that will be displayed in the extension interface.
+
+ Example: https://yourcompany.com/logo.png
+
+ The logo should be a square image (recommended size: 48x48 to 128x128 pixels) in PNG, SVG, or other web-compatible format. The logo will be displayed in the extension popup and blocked page.
+
+
+ Enable debug logging
+
+ This policy controls whether the Check extension generates detailed debug logs for troubleshooting purposes.
+
+ When enabled: Verbose logging is activated and can be viewed in browser developer tools.
+ When disabled (default): Only essential logging occurs.
+
+ Debug logging should only be enabled for troubleshooting as it may impact performance and generate large log files.
+
+
+ Enable debug logging (Chrome)
+
+ This policy controls whether the Check extension generates detailed debug logs for troubleshooting purposes in Google Chrome.
+
+ When enabled: Verbose logging is activated and can be viewed in browser developer tools.
+ When disabled (default): Only essential logging occurs.
+
+ Debug logging should only be enabled for troubleshooting as it may impact performance and generate large log files.
+
+
+ Show valid page badge (Chrome)
+
+ This policy controls whether the Check extension displays a validation badge on legitimate Microsoft login pages in Google Chrome.
+
+ When enabled (default): A green badge or indicator shows when users are on a verified Microsoft login page.
+ When disabled: No visual indicator is shown for valid pages.
+
+
+ Enable page blocking (Chrome)
+
+ This policy controls whether the Check extension blocks access to detected phishing pages in Google Chrome.
+
+ When enabled (default): Suspicious and phishing pages are blocked with a warning screen.
+ When disabled: Pages are only flagged but not blocked (warning mode only).
+
+
+ Enable CIPP reporting (Chrome)
+
+ This policy controls whether the Check extension reports security events to a CIPP (CyberDrain Improved Partner Portal) server in Google Chrome.
+
+ When enabled: Security events are sent to the configured CIPP server for centralized monitoring.
+ When disabled (default): No reporting to external servers occurs.
+
+ Note: Requires CIPP Server URL to be configured.
+
+
+ CIPP server URL (Chrome)
+
+ This policy specifies the base URL for the CIPP server where security events should be reported from Google Chrome.
+
+ Example: https://cipp.yourcompany.com
+
+ This setting is only used when CIPP reporting is enabled. The URL should point to a valid CIPP server instance.
+
+
+ CIPP tenant identifier (Chrome)
+
+ This policy specifies the tenant identifier to include with CIPP alerts for multi-tenant environments from Google Chrome.
+
+ Example: contoso.onmicrosoft.com
+
+ This helps identify which tenant/organization the security event originated from when using a shared CIPP instance.
+
+
+ Custom detection rules URL (Chrome)
+
+ This policy specifies a custom URL from which the extension should fetch detection rules in Google Chrome.
+
+ Example: https://yourcompany.com/detection-rules.json
+
+ When specified, the extension will download detection rules from this URL instead of using the default rules. Leave empty to use default detection rules.
+
+ The URL must serve a valid JSON file matching the detection rules schema.
+
+
+ Detection rules update interval (Chrome)
+
+ This policy specifies how often (in hours) the extension should check for updates to detection rules in Google Chrome.
+
+ Default: 24 hours
+ Range: 1-168 hours (1 hour to 1 week)
+
+ More frequent updates provide better protection but may increase network usage.
+
+
+ Company name (Chrome)
+
+ This policy specifies the company name to display in the extension's user interface for branding purposes in Google Chrome.
+
+ Example: Contoso Corporation
+
+ The company name appears in the extension popup and settings pages.
+
+
+ Company URL (Chrome)
+
+ This policy specifies the company URL used in the extension for branding and navigation purposes in Google Chrome.
+
+ Example: https://contoso.com
+
+ The company URL is used for linking back to the company website from the extension interface.
+
+
+ Product name (Chrome)
+
+ This policy specifies a custom product name for the extension in Google Chrome.
+
+ Example: Contoso Security Guard
+
+ This allows organizations to rebrand the extension with their own product name.
+
+
+ Support email address (Chrome)
+
+ This policy specifies the email address users should contact for support with the extension in Google Chrome.
+
+ Example: security@contoso.com
+
+ This email address is displayed in the extension interface and help documentation.
+
+
+ Primary theme color (Chrome)
+
+ This policy specifies the primary theme color for the extension interface using a hex color code in Google Chrome.
+
+ Example: #0078D4
+
+ The color should be in hex format (e.g., #FF0000 for red, #0078D4 for Microsoft blue). This color is used for UI elements and branding throughout the extension.
+
+
+ Company logo URL (Chrome)
+
+ This policy specifies the URL to a company logo that will be displayed in the extension interface in Google Chrome.
+
+ Example: https://yourcompany.com/logo.png
+
+ The logo should be a square image (recommended size: 48x48 to 128x128 pixels) in PNG, SVG, or other web-compatible format. The logo will be displayed in the extension popup and blocked page.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Update Interval (hours):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Update Interval (hours):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ URL Allowlist (URLs with wildcards or regex patterns):
+
+
+ URL Allowlist (URLs with wildcards or regex patterns):
+
+
+
+
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index a2a1a4e2..d733f8c5 100644
--- a/manifest.json
+++ b/manifest.json
@@ -20,7 +20,7 @@
},
"content_scripts": [
{
- "matches": ["http://*/*", "https://*/*"],
+ "matches": ["http://*/*", "https://*/*", "file:///*/*"],
"js": ["scripts/content.js"],
"css": ["styles/content.css"],
"run_at": "document_idle",
diff --git a/options/options.html b/options/options.html
index 8c19a39f..b5c08329 100644
--- a/options/options.html
+++ b/options/options.html
@@ -115,6 +115,65 @@
Extension Settings
Tenant identifier to include with CIPP alerts for multi-tenant environments