Skip to content

Commit 241c907

Browse files
aklinker1Judimaxwindmillcode0
authored
fix: Automatically convert MV3 content_security_policy to MV2 (#1168)
Co-authored-by: windmillcode0 <[email protected]> Co-authored-by: windmillcode0 <[email protected]>
1 parent aab4244 commit 241c907

File tree

3 files changed

+74
-23
lines changed

3 files changed

+74
-23
lines changed

Diff for: packages/wxt/src/core/utils/__tests__/manifest.test.ts

+35
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,41 @@ describe('Manifest Utils', () => {
15811581
permissions: ['tabs', 'scripting'],
15821582
});
15831583
});
1584+
1585+
it('should convert MV3 CSP object to MV2 CSP string with localhost for MV2', async () => {
1586+
const entrypoints: Entrypoint[] = [];
1587+
const buildOutput = fakeBuildOutput();
1588+
const inputCsp =
1589+
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';";
1590+
const expectedCsp =
1591+
"script-src 'self' 'wasm-unsafe-eval' http://localhost:3000; object-src 'self';";
1592+
1593+
// Setup WXT for Firefox and serve command
1594+
setFakeWxt({
1595+
config: {
1596+
browser: 'firefox',
1597+
command: 'serve',
1598+
manifestVersion: 2,
1599+
manifest: {
1600+
content_security_policy: {
1601+
extension_pages: inputCsp,
1602+
},
1603+
},
1604+
},
1605+
server: fakeWxtDevServer({
1606+
port: 3000,
1607+
hostname: 'localhost',
1608+
origin: 'http://localhost:3000',
1609+
}),
1610+
});
1611+
1612+
const { manifest: actual } = await generateManifest(
1613+
entrypoints,
1614+
buildOutput,
1615+
);
1616+
1617+
expect(actual.content_security_policy).toEqual(expectedCsp);
1618+
});
15841619
});
15851620
});
15861621

Diff for: packages/wxt/src/core/utils/manifest.ts

+35-23
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ export async function generateManifest(
117117
if (wxt.config.manifestVersion === 2) {
118118
convertWebAccessibleResourcesToMv2(manifest);
119119
convertActionToMv2(manifest);
120+
convertCspToMv2(manifest);
120121
moveHostPermissionsToPermissions(manifest);
121122
}
122123

123124
if (wxt.config.manifestVersion === 3) {
124-
validateMv3WebAccessbileResources(manifest);
125+
validateMv3WebAccessibleResources(manifest);
125126
}
126127

127128
stripKeys(manifest);
@@ -143,7 +144,7 @@ export async function generateManifest(
143144
}
144145

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

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

483-
if (wxt.server) {
482+
if (wxt.config.command === 'serve') {
484483
extensionPagesCsp.add('script-src', allowedCsp);
485484
sandboxCsp.add('script-src', allowedCsp);
486485
}
487486

488-
if (manifest.manifest_version === 3) {
489-
manifest.content_security_policy ??= {};
490-
// @ts-expect-error: extension_pages is not typed
491-
manifest.content_security_policy.extension_pages =
492-
extensionPagesCsp.toString();
493-
// @ts-expect-error: sandbox is not typed
494-
manifest.content_security_policy.sandbox = sandboxCsp.toString();
495-
} else {
496-
manifest.content_security_policy = extensionPagesCsp.toString();
497-
}
487+
manifest.content_security_policy ??= {};
488+
// @ts-expect-error: extension_pages is not typed
489+
manifest.content_security_policy.extension_pages =
490+
extensionPagesCsp.toString();
491+
// @ts-expect-error: sandbox is not typed
492+
manifest.content_security_policy.sandbox = sandboxCsp.toString();
498493
}
499494

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

650+
function convertCspToMv2(manifest: Manifest.WebExtensionManifest): void {
651+
if (
652+
typeof manifest.content_security_policy === 'string' ||
653+
manifest.content_security_policy?.extension_pages == null
654+
)
655+
return;
656+
657+
manifest.content_security_policy =
658+
manifest.content_security_policy.extension_pages;
659+
}
660+
655661
/**
656662
* Make sure all resources are in MV3 format. If not, add a wanring
657663
*/
658-
export function validateMv3WebAccessbileResources(
664+
export function validateMv3WebAccessibleResources(
659665
manifest: Manifest.WebExtensionManifest,
660666
): void {
661667
if (manifest.web_accessible_resources == null) return;
@@ -718,3 +724,9 @@ const mv3OnlyKeys = [
718724
'side_panel',
719725
];
720726
const firefoxMv3OnlyKeys = ['host_permissions'];
727+
728+
const DEFAULT_MV3_EXTENSION_PAGES_CSP =
729+
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';";
730+
const DEFAULT_MV3_SANDBOX_CSP =
731+
"sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';";
732+
const DEFAULT_MV2_CSP = "script-src 'self'; object-src 'self';";

Diff for: pnpm-lock.yaml

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)