fix(plugins): allow inline scripts in the sandboxed frame + fix server lint error

- Plugin frame CSP: the frame runs at an opaque origin (sandbox without
  allow-same-origin), so script-src 'self' matches nothing and the widget's own
  script never runs (stuck on 'Loading…'). Allow 'unsafe-inline' — the sandbox,
  not this directive, is the isolation boundary, and the plugin author controls
  the frame code either way.
- Fix a no-constant-binary-expression eslint error in registry.test.ts that was
  failing the server lint:check (eslint .) in CI.
This commit is contained in:
mauriceboe
2026-07-03 18:36:32 +02:00
parent 69a5367bd0
commit a4257f3611
2 changed files with 7 additions and 2 deletions
@@ -71,7 +71,11 @@ export class PluginFrameController {
const connect = ["'self'", ...outbound.map((h) => (h.includes('://') ? h : `https://${h}`))].join(' ');
return [
"default-src 'none'",
"script-src 'self'",
// The frame runs at an OPAQUE origin (sandbox without allow-same-origin),
// so 'self' matches nothing — inline is the only way its own script can run.
// Safe here: the sandbox (not this script-src) is the isolation boundary,
// and the plugin author controls the frame's code either way.
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob:",
"font-src 'self' data:",
@@ -120,7 +120,8 @@ describe('PluginRegistryService', () => {
const row = testDb.prepare("SELECT status, source_repo, source_commit FROM plugins WHERE id='flight-tracker'").get() as { status: string; source_repo: string; source_commit: string };
expect(row).toMatchObject({ status: 'inactive', source_repo: 'acme/trek-flight', source_commit: 'a'.repeat(40) });
// no staging left behind
expect(fs.existsSync(path.join(dataRoot, '.staging'))).toBe(false || fs.readdirSync(path.join(dataRoot, '.staging')).length === 0);
const staging = path.join(dataRoot, '.staging');
expect(!fs.existsSync(staging) || fs.readdirSync(staging).length === 0).toBe(true);
});
it('rejects an sha256 mismatch', async () => {