Skip to content

Commit edbbc1a

Browse files
committed
Fixing bug clicking certain links from emails
1 parent b183239 commit edbbc1a

3 files changed

Lines changed: 192 additions & 31 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,6 @@ logs
140140
*.log
141141
Braver Search/Braver Search.xcodeproj/project.pbxproj
142142
*.pbxproj
143+
144+
# windsurf rules
145+
.windsurfrules

Braver Search/Shared (Extension)/Resources/background.js

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,20 @@ const SEARCH_ENGINE_HOSTS = [
1717
'www.yandex.com'
1818
];
1919

20-
function isSupportedSearchEngine(hostname) {
21-
return SEARCH_ENGINE_HOSTS.some(domain =>
22-
hostname === domain
23-
);
20+
function isSupportedSearchEngine(url) {
21+
// Check if the hostname matches
22+
if (!SEARCH_ENGINE_HOSTS.some(domain => url.hostname === domain)) {
23+
return false;
24+
}
25+
26+
// Check if this is actually a search path
27+
const searchPaths = ['/search', '/web'];
28+
// Root path needs special handling - only redirect if it has a 'q' parameter
29+
if (url.pathname === '/' || url.pathname === '') {
30+
return url.searchParams.has('q');
31+
}
32+
33+
return searchPaths.some(path => url.pathname === path || url.pathname === path + '/');
2434
}
2535

2636
// Function to check if redirect is enabled
@@ -50,32 +60,76 @@ browser.webNavigation.onBeforeNavigate.addListener(async (details) => {
5060

5161
if (details.url) {
5262
console.log("Braver Search: URL detected", details.url);
53-
const url = new URL(details.url);
5463

55-
// Check if this is a supported search engine domain
56-
if (!isSupportedSearchEngine(url.hostname)) {
57-
console.log("Braver Search: Not a supported search engine", url.hostname);
58-
return;
59-
}
60-
61-
const searchQuery = url.searchParams.get('q');
62-
console.log("Braver Search: Search query found", {
63-
url: url.toString(),
64-
hostname: url.hostname,
65-
pathname: url.pathname,
66-
searchQuery
67-
});
68-
69-
if (searchQuery) {
70-
const searchUrl = "https://search.brave.com/search?q=";
71-
const redirectUrl = searchUrl + encodeURIComponent(searchQuery);
72-
console.log("Braver Search: Attempting redirect to", redirectUrl);
64+
try {
65+
const url = new URL(details.url);
66+
67+
// Skip URLs that are too complex or likely not search queries
68+
if (details.url.includes('%2F%2F') || url.pathname.length > 30) {
69+
console.log("Braver Search: Skipping complex URL", details.url);
70+
return;
71+
}
72+
73+
// Check if this is a supported search engine domain and path
74+
if (!isSupportedSearchEngine(url)) {
75+
console.log("Braver Search: Not a supported search engine or path", {
76+
hostname: url.hostname,
77+
pathname: url.pathname
78+
});
79+
return;
80+
}
81+
82+
const searchQuery = url.searchParams.get('q');
83+
console.log("Braver Search: Search query found", {
84+
url: url.toString(),
85+
hostname: url.hostname,
86+
pathname: url.pathname,
87+
searchQuery
88+
});
7389

74-
browser.tabs.update(details.tabId, { url: redirectUrl })
75-
.then(() => console.log("Braver Search: Redirect successful"))
76-
.catch(error => console.error("Braver Search: Redirect failed", error));
77-
} else {
78-
console.log("Braver Search: No search query found in URL");
90+
if (searchQuery) {
91+
// More sophisticated check to distinguish URLs from legitimate searches
92+
const isLikelyURL = (query) => {
93+
// If it's very long, likely a complex URL
94+
if (query.length > 100) return true;
95+
96+
// Check for encoded URL components that indicate a complex URL
97+
if (query.includes('%2F%2F')) return true;
98+
99+
// Check for tracking or redirect URLs
100+
if (query.includes('awstrack.me')) return true;
101+
if (query.includes('tracking=') || query.includes('redirect=')) return true;
102+
103+
// Look for URL-like patterns, but be more lenient with searches
104+
const urlPattern = /^https?:\/\/[\w\.-]+\.[a-z]{2,}(\/[\w\.-]*)*$/i;
105+
if (urlPattern.test(query)) return true;
106+
107+
// Check for complex URL structures (multiple paths and query params)
108+
const hasMultiplePaths = (query.match(/\//g) || []).length > 2;
109+
const hasMultipleQueryParams = (query.match(/[?&][^?&]+=[^?&]+/g) || []).length > 1;
110+
if (hasMultiplePaths && hasMultipleQueryParams) return true;
111+
112+
// Don't block queries that just happen to contain domains or technical terms
113+
return false;
114+
};
115+
116+
if (isLikelyURL(searchQuery)) {
117+
console.log("Braver Search: Query looks like a URL, skipping", searchQuery);
118+
return;
119+
}
120+
121+
const searchUrl = "https://search.brave.com/search?q=";
122+
const redirectUrl = searchUrl + encodeURIComponent(searchQuery);
123+
console.log("Braver Search: Attempting redirect to", redirectUrl);
124+
125+
browser.tabs.update(details.tabId, { url: redirectUrl })
126+
.then(() => console.log("Braver Search: Redirect successful"))
127+
.catch(error => console.error("Braver Search: Redirect failed", error));
128+
} else {
129+
console.log("Braver Search: No search query found in URL");
130+
}
131+
} catch (error) {
132+
console.error("Braver Search: Error processing URL", error);
79133
}
80134
} else {
81135
console.log("Braver Search: No URL in navigation details", details);

Braver Search/Tests/JavaScript/background.test.js

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,127 @@ describe('Background Script', () => {
5555
beforeEach(() => {
5656
browser.storage.local.get.mockImplementation(() => Promise.resolve({ enabled: true }));
5757
});
58-
58+
59+
// Test legitimate search queries that contain URL-like terms
60+
describe('legitimate search queries', () => {
61+
const validSearchQueries = [
62+
'What is http',
63+
'http meaning',
64+
'What is the IP of google.com',
65+
'how to host website on .com domain',
66+
'difference between http and https',
67+
'best .com domain registrar',
68+
'what is localhost:3000',
69+
'how to buy domain.com',
70+
'http vs https security',
71+
'compare .com vs .org',
72+
'what is port 8080 used for',
73+
'localhost not working'
74+
];
75+
76+
validSearchQueries.forEach(query => {
77+
it(`should redirect search: "${query}"`, async () => {
78+
await navigationListener({
79+
url: `https://google.com/search?q=${encodeURIComponent(query)}`
80+
});
81+
82+
expect(browser.tabs.update).toHaveBeenCalledWith(
83+
undefined,
84+
{ url: `https://search.brave.com/search?q=${encodeURIComponent(query)}` }
85+
);
86+
});
87+
});
88+
});
89+
90+
// Test URLs that should not be redirected
91+
describe('complex URLs that should not redirect', () => {
92+
const urlsThatShouldNotRedirect = [
93+
// Email tracking URLs
94+
'https://4bqs42xm.r.us-west-2.awstrack.me/L0/https:%2F%2Fstore.ui.com%2Fus%2Fen%2Forder%2Fstatus/1/123',
95+
// URLs with multiple query parameters
96+
'https://example.com/path?id=123&redirect=https://another.com',
97+
// URLs with encoded components
98+
'https://example.com/redirect?url=https%3A%2F%2Fgoogle.com',
99+
// Complex paths with dots
100+
'https://api.service.com/v1/users/profile.json',
101+
// URLs with tracking parameters
102+
'https://click.email.domain.com/tracking?id=123&url=https://shop.com',
103+
// Deep links
104+
'https://app.domain.com/deep/link/to/content?ref=email',
105+
// URLs with authentication tokens
106+
'https://auth.service.com/callback?token=abc123&redirect_uri=https://app.com',
107+
// URLs with fragments
108+
'https://docs.domain.com/page#section-1?source=email'
109+
];
110+
111+
urlsThatShouldNotRedirect.forEach(url => {
112+
it(`should not redirect URL: "${url}"`, async () => {
113+
await navigationListener({
114+
url: url
115+
});
116+
117+
expect(browser.tabs.update).not.toHaveBeenCalled();
118+
});
119+
});
120+
});
121+
122+
// Test edge cases
123+
describe('edge cases', () => {
124+
const edgeCases = [
125+
{
126+
name: 'search query with IP address',
127+
url: 'https://google.com/search?q=ping 192.168.1.1',
128+
shouldRedirect: true
129+
},
130+
{
131+
name: 'search about localhost',
132+
url: 'https://google.com/search?q=what is localhost:8080',
133+
shouldRedirect: true
134+
},
135+
{
136+
name: 'search with URL in quotes',
137+
url: 'https://google.com/search?q="https://example.com" review',
138+
shouldRedirect: true
139+
},
140+
{
141+
name: 'search about domain extensions',
142+
url: 'https://google.com/search?q=.com vs .org vs .net',
143+
shouldRedirect: true
144+
}
145+
];
146+
147+
edgeCases.forEach(({ name, url, shouldRedirect }) => {
148+
it(`should handle ${name} correctly`, async () => {
149+
await navigationListener({
150+
url: url
151+
});
152+
153+
if (shouldRedirect) {
154+
expect(browser.tabs.update).toHaveBeenCalled();
155+
} else {
156+
expect(browser.tabs.update).not.toHaveBeenCalled();
157+
}
158+
});
159+
});
160+
});
161+
162+
// Original test cases
59163
it('should not redirect Brave Search URLs', async () => {
60164
await navigationListener({
61165
url: 'https://search.brave.com/search?q=test'
62166
});
63167

64168
expect(browser.tabs.update).not.toHaveBeenCalled();
65169
});
66-
170+
67171
it('should not redirect URLs without search queries', async () => {
68172
await navigationListener({
69173
url: 'https://google.com'
70174
});
71175

72176
expect(browser.tabs.update).not.toHaveBeenCalled();
73177
});
74-
178+
75179
it('should properly encode search queries', async () => {
76180
await navigationListener({
77181
url: 'https://google.com/search?q=test search'

0 commit comments

Comments
 (0)