Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions docs/guide/essentials/target-different-browsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ To get the target manifest version at runtime, use the built-time constant provi

## Filtering Entrypoints

Every entrypoint can be included or excluded when targetting specific browsers via the `include` and `exclude` options.
Every entrypoint can be included or excluded when targeting specific browsers via the `include` and `exclude` options.

Here are some examples:

- Content script only built when targetting `firefox`:
- Content script only built when targeting `firefox`:

```ts
export default defineContentScript({
Expand Down
35 changes: 35 additions & 0 deletions packages/wxt/src/core/utils/__tests__/manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,41 @@ describe('Manifest Utils', () => {
permissions: ['tabs', 'scripting'],
});
});

it('should convert MV3 CSP object to MV2 CSP string with localhost for MV2', async () => {
const entrypoints: Entrypoint[] = [];
const buildOutput = fakeBuildOutput();
const inputCsp =
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';";
const expectedCsp =
"script-src 'self' 'wasm-unsafe-eval' http://localhost:3000; object-src 'self';";

// Setup WXT for Firefox and serve command
setFakeWxt({
config: {
browser: 'firefox',
command: 'serve',
manifestVersion: 2,
manifest: {
content_security_policy: {
extension_pages: inputCsp,
},
},
},
server: fakeWxtDevServer({
port: 3000,
hostname: 'localhost',
origin: 'http://localhost:3000',
}),
});

const { manifest: actual } = await generateManifest(
entrypoints,
buildOutput,
);

expect(actual.content_security_policy).toEqual(expectedCsp);
});
});
});

Expand Down
58 changes: 35 additions & 23 deletions packages/wxt/src/core/utils/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,12 @@ export async function generateManifest(
if (wxt.config.manifestVersion === 2) {
convertWebAccessibleResourcesToMv2(manifest);
convertActionToMv2(manifest);
convertCspToMv2(manifest);
moveHostPermissionsToPermissions(manifest);
}

if (wxt.config.manifestVersion === 3) {
validateMv3WebAccessbileResources(manifest);
validateMv3WebAccessibleResources(manifest);
}

stripKeys(manifest);
Expand All @@ -143,7 +144,7 @@ export async function generateManifest(
}

/**
* Removes suffixes from the version, like X.Y.Z-alpha1 (which brosers don't allow), so it's a
* Removes suffixes from the version, like X.Y.Z-alpha1 (which browsers don't allow), so it's a
* simple version number, like X or X.Y or X.Y.Z, which browsers allow.
*/
function simplifyVersion(versionName: string): string {
Expand Down Expand Up @@ -467,34 +468,28 @@ function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
}

const extensionPagesCsp = new ContentSecurityPolicy(
manifest.manifest_version === 3
? // @ts-expect-error: extension_pages is not typed
(manifest.content_security_policy?.extension_pages ??
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';") // default extension_pages CSP for MV3
: (manifest.content_security_policy ??
"script-src 'self'; object-src 'self';"), // default CSP for MV2
// @ts-expect-error: extension_pages exists, we convert MV2 CSPs to this earlier in the process
manifest.content_security_policy?.extension_pages ??
(manifest.manifest_version === 3
? DEFAULT_MV3_EXTENSION_PAGES_CSP
: DEFAULT_MV2_CSP),
);
const sandboxCsp = new ContentSecurityPolicy(
// @ts-expect-error: sandbox is not typed
manifest.content_security_policy?.sandbox ??
"sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';", // default sandbox CSP for MV3
manifest.content_security_policy?.sandbox ?? DEFAULT_MV3_SANDBOX_CSP,
);

if (wxt.server) {
if (wxt.config.command === 'serve') {
extensionPagesCsp.add('script-src', allowedCsp);
sandboxCsp.add('script-src', allowedCsp);
}

if (manifest.manifest_version === 3) {
manifest.content_security_policy ??= {};
// @ts-expect-error: extension_pages is not typed
manifest.content_security_policy.extension_pages =
extensionPagesCsp.toString();
// @ts-expect-error: sandbox is not typed
manifest.content_security_policy.sandbox = sandboxCsp.toString();
} else {
manifest.content_security_policy = extensionPagesCsp.toString();
}
manifest.content_security_policy ??= {};
// @ts-expect-error: extension_pages is not typed
manifest.content_security_policy.extension_pages =
extensionPagesCsp.toString();
// @ts-expect-error: sandbox is not typed
manifest.content_security_policy.sandbox = sandboxCsp.toString();
}

function addDevModePermissions(manifest: Manifest.WebExtensionManifest) {
Expand Down Expand Up @@ -613,7 +608,7 @@ export function stripPathFromMatchPattern(pattern: string) {
/**
* Converts all MV3 web accessible resources to their MV2 forms. MV3 web accessible resources are
* generated in this file, and may be defined by the user in their manifest. In both cases, when
* targetting MV2, automatically convert their definitions down to the basic MV2 array.
* targeting MV2, automatically convert their definitions down to the basic MV2 array.
*/
export function convertWebAccessibleResourcesToMv2(
manifest: Manifest.WebExtensionManifest,
Expand Down Expand Up @@ -652,10 +647,21 @@ function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void {
manifest.browser_action = manifest.action;
}

function convertCspToMv2(manifest: Manifest.WebExtensionManifest): void {
if (
typeof manifest.content_security_policy === 'string' ||
manifest.content_security_policy?.extension_pages == null
)
return;

manifest.content_security_policy =
manifest.content_security_policy.extension_pages;
}

/**
* Make sure all resources are in MV3 format. If not, add a wanring
*/
export function validateMv3WebAccessbileResources(
export function validateMv3WebAccessibleResources(
manifest: Manifest.WebExtensionManifest,
): void {
if (manifest.web_accessible_resources == null) return;
Expand Down Expand Up @@ -718,3 +724,9 @@ const mv3OnlyKeys = [
'side_panel',
];
const firefoxMv3OnlyKeys = ['host_permissions'];

const DEFAULT_MV3_EXTENSION_PAGES_CSP =
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';";
const DEFAULT_MV3_SANDBOX_CSP =
"sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';";
const DEFAULT_MV2_CSP = "script-src 'self'; object-src 'self';";
4 changes: 4 additions & 0 deletions pnpm-lock.yaml

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