Skip to content

Commit

Permalink
Handle the unexpected deactivation of declarative net request rules +…
Browse files Browse the repository at this point in the history
… use v2 API for the list emails endpoint (#40)
  • Loading branch information
dedoussis authored Aug 16, 2023
1 parent 0286985 commit 4f2006a
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 12 deletions.
67 changes: 65 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "icloud-hide-my-email-browser-extension",
"version": "1.1.1",
"version": "1.1.2",
"description": "Cross-browser extension enabling the usage of iCloud's Hide My Email service.",
"license": "MIT",
"author": "dedoussis",
Expand Down Expand Up @@ -35,6 +35,7 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/chrome": "^0.0.243",
"@types/lodash.isequal": "^4.5.6",
"@types/lodash.startcase": "^4.4.7",
"@types/react": "^17.0.39",
Expand Down
54 changes: 54 additions & 0 deletions src/declarativeNetRequestRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const STATIC_RULESET_ID = 'icloud_com_simulation_headers';

const constructRules = (): chrome.declarativeNetRequest.Rule[] => [
{
id: 1,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
header: 'Origin',
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: 'https://www.icloud.com',
},
{
header: 'Referer',
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: 'https://www.icloud.com/',
},
],
},
condition: {
urlFilter: '|https://*.apple.com/*',
resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
excludedInitiatorDomains: ['apple.com', 'icloud.com'],
},
},
{
id: 2,
priority: 1,
action: {
type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
requestHeaders: [
{
header: 'Origin',
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: 'https://www.icloud.com',
},
{
header: 'Referer',
operation: chrome.declarativeNetRequest.HeaderOperation.SET,
value: 'https://www.icloud.com/',
},
],
},
condition: {
urlFilter: '|https://*.icloud.com/*',
resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
excludedInitiatorDomains: ['apple.com', 'icloud.com'],
},
},
];

export { STATIC_RULESET_ID, constructRules };
4 changes: 3 additions & 1 deletion src/iCloudClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,20 @@ export class UpdateFwdToHmeException extends Error {}

export class PremiumMailSettings {
private readonly baseUrl: string;
private readonly v2BaseUrl: string;
constructor(readonly client: ICloudClient) {
if (!client.authenticated) {
throw new ClientAuthenticationError(
'Client is not authenticated. A sing-in is required.'
);
}
this.baseUrl = `${client.webserviceUrl('premiummailsettings')}/v1`;
this.v2BaseUrl = `${client.webserviceUrl('premiummailsettings')}/v2`;
}

async listHme(): Promise<ListHmeResult> {
const response = await this.client.requester.get(
`${this.baseUrl}/hme/list`
`${this.v2BaseUrl}/hme/list`
);
return response.data.result;
}
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"declarative_net_request": {
"rule_resources": [
{
"id": "ruleset_1",
"id": "icloud_com_simulation_headers",
"enabled": true,
"path": "rules.json"
}
Expand Down
72 changes: 68 additions & 4 deletions src/pages/Background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ import {
import browser from 'webextension-polyfill';
import { setupWebRequestListeners } from '../../webRequestUtils';
import { Options } from '../../options';
import {
STATIC_RULESET_ID,
constructRules,
} from '../../declarativeNetRequestRules';

if (browser.webRequest !== undefined) {
setupWebRequestListeners();
Expand Down Expand Up @@ -69,6 +73,57 @@ const getClient = async (withTokenValidation = true): Promise<ICloudClient> => {
return client;
};

const maybeSetupDeclarativeNetworkRules = async (): Promise<void> => {
const mv3Browser = browser as unknown as typeof chrome;
if (mv3Browser.declarativeNetRequest === undefined) {
console.debug(
'declarativeNetRequest not enabled. Likely due to manifest version 2.'
);
return;
}

const enabledStaticRulesets =
await mv3Browser.declarativeNetRequest.getEnabledRulesets();

if (enabledStaticRulesets.length > 0) {
console.debug(
'Found enabled static rulesets. Skipping the creation of dynamic rules.',
{ enabledStaticRulesets }
);
return;
}

console.debug(
'No enabled static ruleset has been found. Attempting to enable static ruleset...'
);
try {
await mv3Browser.declarativeNetRequest.updateEnabledRulesets({
enableRulesetIds: [STATIC_RULESET_ID],
});
console.debug('Static ruleset has successfully been enabled!', {
staticRulesetId: STATIC_RULESET_ID,
});
return;
} catch (e) {
console.debug('Failed to enable the static ruleset', {
staticRulesetId: STATIC_RULESET_ID,
errorMessage: e.message,
});
}

const rules = constructRules();
const updateRuleOptions: chrome.declarativeNetRequest.UpdateRuleOptions = {
// potential existing rules are deleted to not exceed the MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES
removeRuleIds: rules.map((rule) => rule.id),
addRules: rules,
};
console.debug(
'Falling back to dynamic rules. Updating dynamic rules...',
updateRuleOptions
);
await mv3Browser.declarativeNetRequest.updateDynamicRules(updateRuleOptions);
};

// ===== Message handling =====

browser.runtime.onMessage.addListener(async (message: Message<unknown>) => {
Expand All @@ -77,6 +132,13 @@ browser.runtime.onMessage.addListener(async (message: Message<unknown>) => {
{
const { email, password } = message.data as LogInRequestData;
const client = await getClient(false);
try {
await maybeSetupDeclarativeNetworkRules();
} catch (e) {
console.error('Failed to setup declarative network rules', {
errorMessage: e.message,
});
}

try {
await client.signIn(email, password);
Expand All @@ -96,10 +158,12 @@ browser.runtime.onMessage.addListener(async (message: Message<unknown>) => {
STATE_MACHINE_TRANSITIONS[PopupState.SignedOut][action];

await setBrowserStorageValue(POPUP_STATE_STORAGE_KEYS, newState);
browser.runtime.sendMessage({
type: MessageType.LogInResponse,
data: { success: true, action },
} as Message<LogInResponseData>);
browser.runtime
.sendMessage({
type: MessageType.LogInResponse,
data: { success: true, action },
} as Message<LogInResponseData>)
.catch(console.debug);

browser.contextMenus
.update(CONTEXT_MENU_ITEM_ID, {
Expand Down
2 changes: 1 addition & 1 deletion src/webRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const setupWebRequestListeners = () => {
urls: [
'https://idmsa.apple.com/appleauth/auth/*',
'https://setup.icloud.com/setup/ws/1/*',
'https://*.icloud.com/v1/hme/*',
'https://*.icloud.com/v*/hme/*',
],
},
['requestHeaders', 'blocking']
Expand Down
3 changes: 1 addition & 2 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ const webpack = require('webpack'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
TerserPlugin = require('terser-webpack-plugin'),
MiniCssExtractPlugin = require('mini-css-extract-plugin'),
{ CleanWebpackPlugin } = require('clean-webpack-plugin'),
ASSET_PATH = process.env.ASSET_PATH || '/',
MANIFEST_VERSION = parseInt(process.env.MANIFEST_VERSION || '3');

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const isDev = process.env.NODE_ENV !== 'production';

const fileExtensions = [
Expand Down

0 comments on commit 4f2006a

Please sign in to comment.