36 Commits
1.1.0 ... 1.1.4

Author SHA1 Message Date
William Oldham
36326d3ecf Merge pull request #29 from movie-web/dev
Version 1.1.4 - Fix permission request page styling
2024-04-05 21:29:48 +01:00
William Oldham
3f33d9bc4a Merge pull request #30 from movie-web/fix-android
Fix Android
2024-04-05 21:27:36 +01:00
William Oldham
86a659f478 Add explicit support for Firefox Android 2024-04-05 21:24:00 +01:00
William Oldham
dbdbf10c5d Close the prompt popup when clicking the button 2024-04-05 21:23:49 +01:00
William Oldham
6fca55a6c3 Bump version 2024-03-28 20:33:47 +00:00
William Oldham
50eadd3a14 Merge pull request #28 from movie-web/fix/#1047
fix ts error, fix styling on PermissionRequest
2024-03-28 20:33:24 +00:00
Jorrin
b2ba74bd97 fix ts error, fix styling on PermissionRequest 2024-03-28 20:26:09 +01:00
William Oldham
ec7d9c1237 Merge pull request #26 from movie-web/dev
Version 1.1.3: Remove Lonelil as default redirect, fix centering and fix type issues
2024-03-15 19:58:15 +00:00
William Oldham
bb61fef30a Merge pull request #25 from movie-web/remove-lonelil
Remove Lonelil, fix centering and enable strict mode
2024-03-15 19:44:19 +00:00
William Oldham
8e179e4455 Remove any in error handling 2024-03-15 19:42:51 +00:00
William Oldham
327f175241 Do safe checking on exception message 2024-03-15 19:39:03 +00:00
William Oldham
c72ec41228 Bump version 2024-03-15 19:24:07 +00:00
William Oldham
0bd1a39b53 Remove react import 2024-03-15 19:24:03 +00:00
William Oldham
de0a1b3849 Fix centering of permission grant page 2024-03-15 18:53:44 +00:00
William Oldham
8ba0544468 Enable TS strict mode and fix all associated errors 2024-03-15 18:49:12 +00:00
William Oldham
a884c785f0 Remove lonelil as a redirect, Firefox didn't like 2024-03-15 18:48:46 +00:00
William Oldham
0801f64e1b Fix content type for extension deployment 2024-03-05 21:13:59 +00:00
William Oldham
8735f7b4b2 Update deploying.yml 2024-03-05 21:13:22 +00:00
William Oldham
a2eef13d28 Merge pull request #24 from movie-web/dev
Version 1.1.2
2024-03-05 21:08:35 +00:00
William Oldham
90f941431c Bump extension to 1.1.2 2024-03-05 18:51:26 +00:00
William Oldham
df05547581 Merge pull request #23 from movie-web/feature/#867
add inactive icon if the domain is not whitelisted
2024-03-05 18:51:01 +00:00
William Oldham
a8e3de1451 Update inactive logo and bold enabled/disabled 2024-03-05 18:46:37 +00:00
Jorrin
1801ee238d Delete icon.development.png 2024-03-04 21:51:15 +01:00
William Oldham
434b2475cb Merge pull request #22 from movie-web/feature/update-default-domains
update default domain whitelist, remove movie-web references
2024-03-04 20:49:24 +00:00
William Oldham
50166457b8 Merge pull request #21 from movie-web/fix/#952
add firstPartyDomain for first-party isolation
2024-03-04 20:48:19 +00:00
Jorrin
e2ea8a3c7e remove unused useEffect 2024-03-04 21:45:37 +01:00
Jorrin
213828e818 add inactive icon if the domain is not whitelisted 2024-03-03 22:56:13 +01:00
Jorrin
755bba3e1e update default domain whitelist, remove movie-web references 2024-03-03 20:20:34 +01:00
Jorrin
e7ca90b75f add firstPartyDomain for first-party isolation 2024-03-03 20:14:56 +01:00
Jorrin
d74f0abbf6 Merge pull request #20 from movie-web/fix/improve-error-message
Improve "Domain is not whitelisted" error message
2024-03-03 13:45:24 +01:00
Jorrin
cade0b50ab Add more details to the error message 2024-03-01 21:59:38 +01:00
Jorrin
1ac8147cdd add instruction to domain whitelist error message 2024-03-01 21:57:18 +01:00
William Oldham
01a03cfeb8 Merge pull request #19 from movie-web/dev
Version 1.1.1 - Apply CORS header rules to all requests regardless of MW set headers
2024-02-21 18:57:54 +00:00
William Oldham
6d1fa16553 Bump version 2024-02-21 18:53:08 +00:00
William Oldham
a8417c75a3 Merge pull request #18 from movie-web/fix/#904
always create dynamic rule for fetch request
2024-02-21 18:50:40 +00:00
Jorrin
abf26103e1 always create dynamic rule for fetch request 2024-02-15 14:17:08 +01:00
22 changed files with 112 additions and 48 deletions

10
.github/SECURITY.md vendored
View File

@@ -2,13 +2,9 @@
## Supported Versions ## Supported Versions
The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app. The latest version of movie-web is the only version that is supported, as it is the only version that is being actively developed.
This published version is equivalent to the master branch.
Support is not provided for any forks or mirrors of movie-web.
## Reporting a Vulnerability ## Reporting a Vulnerability
There are two ways you can contact the movie-web maintainers to report a vulnerability: You can contact the movie-web maintainers to report a vulnerability:
- Email [security@movie-web.app](mailto:security@movie-web.app) - Report the vulnerability in the [movie-web Discord server](https://movie-web.github.io/links/discord)
- Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app)

View File

@@ -94,8 +94,8 @@ jobs:
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./chrome/chrome-mv3-prod.zip asset_path: ./chrome/chrome-mv3-prod.zip
asset_name: extension-mw.chrome.crx asset_name: extension-mw.chrome.zip
asset_content_type: application/x-chrome-extension asset_content_type: application/zip
- name: Upload Firefox release - name: Upload Firefox release
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1

BIN
assets/active.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
assets/inactive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -1,7 +1,7 @@
{ {
"name": "@movie-web/extension", "name": "@movie-web/extension",
"displayName": "movie-web extension", "displayName": "movie-web extension",
"version": "1.1.0", "version": "1.1.4",
"description": "Enhance your movie-web experience with just one click", "description": "Enhance your movie-web experience with just one click",
"author": "movie-web", "author": "movie-web",
"scripts": { "scripts": {
@@ -53,7 +53,21 @@
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}" "id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}"
},
"gecko_android": {
"id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}"
} }
} },
"web_accessible_resources": [
{
"resources": [
"assets/active.png",
"assets/inactive.png"
],
"matches": [
"<all_urls>"
]
}
]
} }
} }

View File

@@ -14,6 +14,8 @@ type Response = BaseResponse<{
const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (req, res) => { const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (req, res) => {
try { try {
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
const version = getVersion(); const version = getVersion();
res.send({ res.send({
success: true, success: true,
@@ -24,7 +26,7 @@ const handler: PlasmoMessaging.MessageHandler<BaseRequest, Response> = async (re
} catch (err) { } catch (err) {
res.send({ res.send({
success: false, success: false,
error: err.message, error: err instanceof Error ? err.message : String(err),
}); });
} }
}; };

View File

@@ -3,6 +3,7 @@ import type { PlasmoMessaging } from '@plasmohq/messaging';
import type { BaseRequest } from '~types/request'; import type { BaseRequest } from '~types/request';
import type { BaseResponse } from '~types/response'; import type { BaseResponse } from '~types/response';
import { removeDynamicRules, setDynamicRules } from '~utils/declarativeNetRequest'; import { removeDynamicRules, setDynamicRules } from '~utils/declarativeNetRequest';
import { isFirefox } from '~utils/extension';
import { makeFullUrl } from '~utils/fetcher'; import { makeFullUrl } from '~utils/fetcher';
import { assertDomainWhitelist } from '~utils/storage'; import { assertDomainWhitelist } from '~utils/storage';
@@ -31,7 +32,7 @@ type Response<T> = BaseResponse<{
const mapBodyToFetchBody = (body: Request['body'], bodyType: Request['bodyType']): BodyInit => { const mapBodyToFetchBody = (body: Request['body'], bodyType: Request['bodyType']): BodyInit => {
if (bodyType === 'FormData') { if (bodyType === 'FormData') {
const formData = new FormData(); const formData = new FormData();
body.forEach(([key, value]) => { body.forEach(([key, value]: [any, any]) => {
formData.append(key, value.toString()); formData.append(key, value.toString());
}); });
} }
@@ -49,16 +50,17 @@ const mapBodyToFetchBody = (body: Request['body'], bodyType: Request['bodyType']
const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (req, res) => { const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (req, res) => {
try { try {
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
if (!req.body) throw new Error('No request body found in the request.');
const url = makeFullUrl(req.body.url, req.body); const url = makeFullUrl(req.body.url, req.body);
await assertDomainWhitelist(req.sender.tab.url); await assertDomainWhitelist(req.sender.tab.url);
if (Object.keys(req.body.headers).length > 0) { await setDynamicRules({
await setDynamicRules({ ruleId: MAKE_REQUEST_DYNAMIC_RULE,
ruleId: MAKE_REQUEST_DYNAMIC_RULE, targetDomains: [new URL(url).hostname],
targetDomains: [new URL(url).hostname], requestHeaders: req.body.headers,
requestHeaders: req.body.headers, });
});
}
const response = await fetch(url, { const response = await fetch(url, {
method: req.body.method, method: req.body.method,
@@ -71,6 +73,9 @@ const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (r
const cookies = await (chrome || browser).cookies.getAll({ const cookies = await (chrome || browser).cookies.getAll({
url: response.url, url: response.url,
...(isFirefox() && {
firstPartyDomain: new URL(response.url).hostname,
}),
}); });
res.send({ res.send({
@@ -89,7 +94,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, Response<any>> = async (r
console.error('failed request', err); console.error('failed request', err);
res.send({ res.send({
success: false, success: false,
error: err.message, error: err instanceof Error ? err.message : String(err),
}); });
} }
}; };

View File

@@ -11,6 +11,9 @@ type Request = BaseRequest & {
const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => { const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => {
try { try {
if (!req.sender?.tab?.id) throw new Error('No tab ID found in the request.');
if (!req.body) throw new Error('No body found in the request.');
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.set('redirectUrl', req.body.redirectUrl); searchParams.set('redirectUrl', req.body.redirectUrl);
const url = (chrome || browser).runtime.getURL(`/tabs/${req.body.page}.html?${searchParams.toString()}`); const url = (chrome || browser).runtime.getURL(`/tabs/${req.body.page}.html?${searchParams.toString()}`);
@@ -29,7 +32,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
} catch (err) { } catch (err) {
res.send({ res.send({
success: false, success: false,
error: err.message, error: err instanceof Error ? err.message : String(err),
}); });
} }
}; };

View File

@@ -15,6 +15,9 @@ interface Request extends BaseRequest {
const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => { const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (req, res) => {
try { try {
if (!req.sender?.tab?.url) throw new Error('No tab URL found in the request.');
if (!req.body) throw new Error('No request body found in the request.');
await assertDomainWhitelist(req.sender.tab.url); await assertDomainWhitelist(req.sender.tab.url);
await setDynamicRules(req.body); await setDynamicRules(req.body);
res.send({ res.send({
@@ -23,7 +26,7 @@ const handler: PlasmoMessaging.MessageHandler<Request, BaseResponse> = async (re
} catch (err) { } catch (err) {
res.send({ res.send({
success: false, success: false,
error: err.message, error: err instanceof Error ? err.message : String(err),
}); });
} }
}; };

View File

@@ -9,6 +9,7 @@ export function SetupScreen() {
const open = useCallback(() => { const open = useCallback(() => {
const url = (chrome || browser).runtime.getURL(`/tabs/PermissionRequest.html`); const url = (chrome || browser).runtime.getURL(`/tabs/PermissionRequest.html`);
(chrome || browser).tabs.create({ url }); (chrome || browser).tabs.create({ url });
window.close();
}, []); }, []);
return ( return (

View File

@@ -36,7 +36,7 @@ export function ToggleButton(props: ToggleButtonProps) {
</button> </button>
</div> </div>
<p> <p>
Extension {props.active ? 'enabled' : 'disabled'} <br /> on <strong>{props.domain}</strong> Extension <strong>{props.active ? 'enabled' : 'disabled'}</strong> <br /> on <strong>{props.domain}</strong>
</p> </p>
</div> </div>
); );

View File

@@ -15,5 +15,5 @@ export function useDomain(): null | string {
}; };
}, []); }, []);
return makeUrlIntoDomain(domain); return domain ? makeUrlIntoDomain(domain) : null;
} }

View File

@@ -8,12 +8,12 @@ export function useDomainWhitelist() {
const removeDomain = useCallback((domain: string | null) => { const removeDomain = useCallback((domain: string | null) => {
if (!domain) return; if (!domain) return;
setDomainWhitelist((s) => [...s.filter((v) => v !== domain)]); setDomainWhitelist((s) => [...(s ?? []).filter((v) => v !== domain)]);
}, []); }, []);
const addDomain = useCallback((domain: string | null) => { const addDomain = useCallback((domain: string | null) => {
if (!domain) return; if (!domain) return;
setDomainWhitelist((s) => [...s.filter((v) => v !== domain), domain]); setDomainWhitelist((s) => [...(s ?? []).filter((v) => v !== domain), domain]);
}, []); }, []);
return { return {
@@ -23,10 +23,15 @@ export function useDomainWhitelist() {
}; };
} }
export function useToggleWhitelistDomain(domain: string) { export function useToggleWhitelistDomain(domain: string | null) {
const { domainWhitelist, addDomain, removeDomain } = useDomainWhitelist(); const { domainWhitelist, addDomain, removeDomain } = useDomainWhitelist();
const isWhitelisted = domainWhitelist.includes(domain); const isWhitelisted = domainWhitelist.includes(domain ?? '');
const { grantPermission } = usePermission(); const { grantPermission } = usePermission();
const iconPath = (chrome || browser).runtime.getURL(isWhitelisted ? 'assets/active.png' : 'assets/inactive.png');
(chrome || browser).action.setIcon({
path: iconPath,
});
const toggle = useCallback(() => { const toggle = useCallback(() => {
if (!isWhitelisted) { if (!isWhitelisted) {

View File

@@ -28,7 +28,7 @@ function IndexPopup() {
) : ( ) : (
<Frame> <Frame>
<div className="popup"> <div className="popup">
{page === 'toggle' ? <ToggleButton active={isWhitelisted} onClick={toggle} domain={domain} /> : null} {page === 'toggle' && domain ? <ToggleButton active={isWhitelisted} onClick={toggle} domain={domain} /> : null}
{page === 'disabled' ? <DisabledScreen /> : null} {page === 'disabled' ? <DisabledScreen /> : null}
<BottomLabel /> <BottomLabel />
</div> </div>

View File

@@ -13,7 +13,7 @@ body {
#__plasmo { #__plasmo {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100%; height: 100%;
background-color: #0A0A10; background-color: #0A0A10;
} }

View File

@@ -8,11 +8,27 @@ export default function PermissionGrant() {
const { grantPermission } = usePermission(); const { grantPermission } = usePermission();
const queryParams = new URLSearchParams(window.location.search); const queryParams = new URLSearchParams(window.location.search);
const redirectUrl = queryParams.get('redirectUrl') ?? 'https://movie-web.app'; const redirectUrl = queryParams.get('redirectUrl') ?? undefined;
const domain = makeUrlIntoDomain(redirectUrl); const domain = redirectUrl ? makeUrlIntoDomain(redirectUrl) : undefined;
if (!domain) {
return (
<div className="permission-grant container">
<div className="inner-container">
<div className="permission-card">
<h1 className="color-white">Permission</h1>
<p className="text-color" style={{ textAlign: 'center' }}>
No domain found to grant permission to.
</p>
</div>
</div>
</div>
);
}
const redirectBack = () => { const redirectBack = () => {
chrome.tabs.getCurrent((tab) => { chrome.tabs.getCurrent((tab) => {
if (!tab?.id) return;
chrome.tabs.update(tab.id, { url: redirectUrl }); chrome.tabs.update(tab.id, { url: redirectUrl });
}); });
}; };

View File

@@ -10,6 +10,10 @@ body {
padding-bottom: 50px; padding-bottom: 50px;
} }
#__plasmo {
height: unset;
}
.permission-request.container { .permission-request.container {
width: 90%; width: 90%;
margin: 100px auto; margin: 100px auto;

View File

@@ -123,5 +123,6 @@ export const removeDynamicRules = async (ruleIds: number[]) => {
await (chrome || browser).declarativeNetRequest.updateDynamicRules({ await (chrome || browser).declarativeNetRequest.updateDynamicRules({
removeRuleIds: ruleIds, removeRuleIds: ruleIds,
}); });
if ((chrome || browser).runtime.lastError?.message) throw new Error((chrome || browser).runtime.lastError.message); if ((chrome || browser).runtime.lastError?.message)
throw new Error((chrome || browser).runtime.lastError?.message ?? 'Unknown error');
}; };

View File

@@ -1,3 +1,11 @@
export const isChrome = () => { export const isChrome = () => {
return chrome.runtime.getURL('').startsWith('chrome-extension://'); return chrome.runtime.getURL('').startsWith('chrome-extension://');
}; };
export const isFirefox = () => {
try {
return browser.runtime.getURL('').startsWith('moz-extension://');
} catch {
return false;
}
};

View File

@@ -3,7 +3,14 @@ import { useStorage } from '@plasmohq/storage/hook';
import { makeUrlIntoDomain } from '~utils/domains'; import { makeUrlIntoDomain } from '~utils/domains';
export const DEFAULT_DOMAIN_WHITELIST = ['movie-web.app', 'dev.movie-web.app']; export const DEFAULT_DOMAIN_WHITELIST = [
'mw.lonelil.ru',
'watch.qtchaos.de',
'bmov.vercel.app',
'stream.thehairy.me',
'scootydooter.vercel.app',
'movie-web-me.vercel.app',
];
export const storage = new Storage(); export const storage = new Storage();
@@ -31,5 +38,9 @@ export const isDomainWhitelisted = async (url: string | undefined) => {
export const assertDomainWhitelist = async (url: string) => { export const assertDomainWhitelist = async (url: string) => {
const isWhiteListed = await isDomainWhitelisted(url); const isWhiteListed = await isDomainWhitelisted(url);
if (!isWhiteListed) throw new Error('Domain is not whitelisted'); const currentDomain = makeUrlIntoDomain(url);
if (!isWhiteListed)
throw new Error(
`${currentDomain} is not whitelisted. Open the extension and click on the power button to whitelist the site.`,
);
}; };

View File

@@ -1,7 +1,7 @@
import { isChrome } from './extension'; import { isChrome } from './extension';
export function queryCurrentDomain(cb: (domain: string | null) => void) { export function queryCurrentDomain(cb: (domain: string | null) => void) {
const handle = (tabUrl: string | null) => { const handle = (tabUrl: string | undefined) => {
if (!tabUrl) cb(null); if (!tabUrl) cb(null);
else cb(tabUrl); else cb(tabUrl);
}; };

View File

@@ -1,18 +1,13 @@
{ {
"extends": "plasmo/templates/tsconfig.base", "extends": "plasmo/templates/tsconfig.base",
"exclude": [ "exclude": ["node_modules"],
"node_modules" "include": [".plasmo/index.d.ts", "./**/*.ts", "./**/*.tsx"],
],
"include": [
".plasmo/index.d.ts",
"./**/*.ts",
"./**/*.tsx"
],
"compilerOptions": { "compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"paths": { "paths": {
"~*": [ "~*": ["./src/*"]
"./src/*"
]
}, },
"baseUrl": "." "baseUrl": "."
} }