Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions docs/features/domain-squatting-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ You don't need to do anything - the protection works automatically in the backgr

### Page Blocking Control

Check has an **"Enable Page Blocking"** setting in the extension options that controls how suspicious pages are handled:
Check has an **"Enable Page Blocking"** setting in the extension options that controls how suspicious pages are handled. The detection **Action** can be one of three values: `block`, `warn`, or `log`.

- **Page Blocking Enabled** + **Action: "block"** = Page is completely blocked with full-page warning
- **Page Blocking Enabled** + **Action: "warn"** = Warning banner shown, page remains accessible
- **Page Blocking Disabled** = Warning banner shown regardless of action setting (never blocks)
- **Action: "log"** = Detection is recorded in Activity Logs and sent to reporting and webhooks only. No banner and no block are shown to the user, regardless of the Page Blocking setting.
- **Page Blocking Disabled** = Never blocks. A `block` or `warn` action shows a warning banner instead; a `log` action stays silent.
Comment thread
MWG-Logan marked this conversation as resolved.
Outdated

This gives you control over whether you want aggressive blocking or just warnings for suspicious domains.
This gives you control over whether you want aggressive blocking, visible warnings, or silent monitoring for suspicious domains.

### For Advanced Users and IT Departments

Expand All @@ -109,7 +110,7 @@ Edit your `rules/detection-rules.json` file to customize:
{
"domain_squatting": {
"enabled": false, // Turn detection on/off (default: false)
"action": "block" // Action when detected: "block" or "warn"
"action": "block" // Action when detected: "block", "warn", or "log"
}
}
```
Expand All @@ -118,7 +119,7 @@ Edit your `rules/detection-rules.json` file to customize:
```json
{
"domain_squatting": {
"action": "block" // "block" = full page block, "warn" = banner only
"action": "block" // "block" = full page block, "warn" = banner only, "log" = silent, telemetry only
}
}
```
Expand Down
14 changes: 12 additions & 2 deletions options/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -1371,9 +1371,19 @@ class CheckOptions {
if (!escaped.startsWith('^')) {
escaped = '^' + escaped;
}
// Add end anchor if pattern doesn't end with wildcard
// Add the end anchor if the pattern does not already end with a wildcard.
// Only a host or root URL pattern (no path beyond an optional single
// trailing slash) gets the tolerant trailing matcher, so patterns that
// include an explicit path stay exact matches and allowlist entries are
// not silently broadened into prefix matches. Kept in sync with the copy
// in scripts/content.js.
if (!pattern.endsWith('*') && !escaped.endsWith('.*')) {
escaped = escaped + '$';
const afterScheme = pattern.replace(/^https?:\/\//i, '');
const firstSlash = afterScheme.indexOf('/');
const isHostOrRoot = firstSlash === -1 || firstSlash === afterScheme.length - 1;
escaped = isHostOrRoot
? escaped.replace(/\/$/, '') + '(?:[/?#].*)?$'
: escaped + '$';
}
return escaped;
}
Expand Down
49 changes: 39 additions & 10 deletions scripts/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -3411,9 +3411,25 @@ if (window.checkExtensionLoaded) {
escaped = "^" + escaped;
}

// Add end anchor if pattern doesn't end with wildcard
// Add the end anchor if the pattern does not already end with a wildcard.
//
// Only a host or root URL pattern (no path segment beyond an optional
// single trailing slash) is given the tolerant trailing matcher, so that
// allowlisting a host or root URL also matches deep links such as
// https://host/path. A pattern that includes an explicit path stays an
// exact match, so allowlist entries are not silently broadened into prefix
// matches (for example "https://host/safe" must not also allow
// "https://host/safe/anything"). Suffix tricks such as
// "https://host.evil.com/" still do not match a "https://host/" entry,
// because the tolerated remainder must begin with /, ?, or #.
if (!pattern.endsWith("*") && !escaped.endsWith(".*")) {
escaped = escaped + "$";
const afterScheme = pattern.replace(/^https?:\/\//i, "");
const firstSlash = afterScheme.indexOf("/");
const isHostOrRoot =
firstSlash === -1 || firstSlash === afterScheme.length - 1;
escaped = isHostOrRoot
? escaped.replace(/\/$/, "") + "(?:[/?#].*)?$"
: escaped + "$";
}

return escaped;
Expand Down Expand Up @@ -4247,19 +4263,29 @@ if (window.checkExtensionLoaded) {

// Check if notifications should be shown
const showNotifications = config.showNotifications !== false;

// Determine if we should block the page
// Requires: 1) enablePageBlocking is ON, 2) domain_squatting action is "block"

// Resolve the effective action as a real three-state value:
// 'block' | 'warn' | 'log'. Previously this only checked for
// 'block', which collapsed 'log' into 'warn' (banner + "warned").
// Semantics: warn logs telemetry AND shows a banner; log logs
// telemetry only and shows nothing to the user.
const squattingAction = squattingData.action || 'warn';
logger.debug(` enablePageBlocking: ${config.enablePageBlocking}`);
logger.debug(` squattingData.action: ${squattingData.action}`);
const shouldBlock = squattingData.action === 'block' &&
const shouldBlock = squattingAction === 'block' &&
config.enablePageBlocking !== false;
const outcome = shouldBlock
? "blocked"
: squattingAction === 'log'
? "logged"
: "warned";
logger.debug(` shouldBlock: ${shouldBlock}`);

logger.debug(` outcome: ${outcome}`);

// Log domain squatting detection
logProtectionEvent({
type: "threat_detected",
action: shouldBlock ? "blocked" : "warned",
action: outcome,
url: location.href,
origin: currentOrigin,
reason: `Domain squatting detected: ${squattingData.techniques.map(t => t.description).join('; ')}`,
Expand All @@ -4282,7 +4308,7 @@ if (window.checkExtensionLoaded) {
})),
severity: squattingData.severity,
confidence: squattingData.confidence,
action: shouldBlock ? "blocked" : "warned",
action: outcome,
reason: `Domain squatting detected: ${squattingData.techniques.map(t => t.description).join('; ')}`
});

Expand All @@ -4301,7 +4327,7 @@ if (window.checkExtensionLoaded) {
})),
severity: squattingData.severity,
confidence: squattingData.confidence,
action: shouldBlock ? "blocked" : "warned",
action: outcome,
reason: `Domain squatting detected: ${squattingData.techniques.map(t => t.description).join('; ')}`
},
})
Expand Down Expand Up @@ -4330,6 +4356,9 @@ if (window.checkExtensionLoaded) {
}
);
return; // Stop processing, page is blocked
} else if (squattingAction === 'log') {
// Log-only action: telemetry has already been emitted above.
// Intentionally show nothing to the user. Log must not warn.
} else if (showNotifications) {
// Show warning banner for domain squatting (only if notifications enabled)
const techniquesDesc = squattingData.techniques.map(t =>
Expand Down
Loading