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
12 changes: 12 additions & 0 deletions .github/workflows/audit.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
name: audit

on:
push:
branches: [main]
paths:
- core/package.json
- core/package-lock.json
- .github/workflows/audit.yml
pull_request:
branches: [main]
paths:
- core/package.json
- core/package-lock.json
- .github/workflows/audit.yml
schedule:
# Nightly, so a freshly disclosed advisory surfaces without a push.
- cron: "14 3 * * *"
Expand Down
12 changes: 6 additions & 6 deletions core/package-lock.json

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

2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"playwright": "^1.63.0",
"typescript": "^5.5.3",
"typescript-eslint": "^8.71.0",
"vite": "^8.3.2",
"vite": "^8.3.4",
"vitest": "^5.0.3"
},
"overrides": {
Expand Down
32 changes: 31 additions & 1 deletion core/src/lib/messaging.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { sendToContent } from './messaging';
import { ensureContentScript, sendToContent } from './messaging';

afterEach(() => {
vi.unstubAllGlobals();
});

describe('ensureContentScript', () => {
it('injects the standalone bundle when the receiver is missing', async () => {
const sendMessage = vi.fn()
.mockRejectedValueOnce(new Error('Receiving end does not exist'))
.mockResolvedValue({ kind: 'ok' });
const executeScript = vi.fn().mockResolvedValue([]);
vi.stubGlobal('chrome', { tabs: { sendMessage }, scripting: { executeScript } });

await ensureContentScript(42);

expect(executeScript).toHaveBeenCalledWith({
target: { tabId: 42 },
files: ['src/content/content-script.js'],
});
expect(sendMessage).toHaveBeenCalledTimes(2);
});

it('keeps an already responding receiver', async () => {
const executeScript = vi.fn();
vi.stubGlobal('chrome', {
tabs: { sendMessage: vi.fn().mockResolvedValue({ kind: 'ok' }) },
scripting: { executeScript },
});

await ensureContentScript(42);

expect(executeScript).not.toHaveBeenCalled();
});
});

describe('sendToContent', () => {
it('addresses the main frame so a hidden service iframe cannot answer first', async () => {
// Without an explicit frameId, chrome delivers to every frame and resolves with whichever
Expand Down
2 changes: 1 addition & 1 deletion core/src/lib/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export async function ensureContentScript(tabId: number): Promise<void> {

await chrome.scripting.executeScript({
target: { tabId },
files: ['assets/content-script.ts-loader.js'],
files: ['src/content/content-script.js'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Inject the emitted standalone bundle path

When the receiver is absent—such as on a tab left open across an extension install or reload—the standalone entry is emitted according to entryFileNames: 'assets/[name].js', making this bundle assets/content-script.js; the build does not create src/content/content-script.js. Consequently, chrome.scripting.executeScript rejects instead of restoring the content script, so subsequent tab operations fail. Reference the actual emitted filename (preferably via the bundler's IIFE/script import mechanism) rather than this source-like path.

Useful? React with 👍 / 👎.

});

const started = Date.now();
Expand Down
6 changes: 5 additions & 1 deletion core/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export default defineConfig({
plugins: [],
},
},
plugins: [react(), crx({ manifest }), ortRuntime()],
plugins: [react(), crx({
manifest,
// Avoid the module loader's cross-origin import from pages such as YouTube.
contentScripts: { standaloneFiles: ['src/content/content-script.ts'] },
}), ortRuntime()],
build: {
target: 'esnext',
sourcemap: true,
Expand Down
Loading