25 Commits
1.1.1 ... 1.1.3

Author SHA1 Message Date
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
19 changed files with 96 additions and 38 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.1", "version": "1.1.3",
"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": {
@@ -54,6 +54,17 @@
"gecko": { "gecko": {
"id": "{3fd86354-c73f-4395-9e26-2c5c984579bf}" "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,6 +50,9 @@ 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);
@@ -69,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({
@@ -87,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

@@ -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 {
@@ -27,6 +27,11 @@ export function useToggleWhitelistDomain(domain: string) {
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

@@ -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

@@ -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": "."
} }