Skip to content
Open
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
2 changes: 1 addition & 1 deletion manifest.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"https://lh3.google.com/*",
"https://*.ggpht.com/*"
],
"optional_host_permissions": ["<all_urls>"],
"optional_host_permissions": ["https://chatgpt.com/*", "<all_urls>"],
"background": {
"service_worker": "src/pages/background/index.ts",
"type": "module"
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"https://lh3.google.com/*",
"https://*.ggpht.com/*"
],
"optional_host_permissions": ["<all_urls>"],
"optional_host_permissions": ["https://chatgpt.com/*", "<all_urls>"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add https://chat.openai.com/* to the optional ChatGPT contract.

The PR objective includes both ChatGPT domains, but the manifests and regression test cover only https://chatgpt.com/*. Add the second host to both manifests and assert both hosts in the opt-in test.

  • manifest.json#L44-L44: add https://chat.openai.com/*.
  • manifest.dev.json#L58-L58: mirror the production permission list.
  • src/core/utils/__tests__/manifestPermissions.test.ts#L82-L91: assert both hosts and both content-script exclusions.
📍 Affects 3 files
  • manifest.json#L44-L44 (this comment)
  • manifest.dev.json#L58-L58
  • src/core/utils/__tests__/manifestPermissions.test.ts#L82-L91
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@manifest.json` at line 44, Add https://chat.openai.com/* to the
optional_host_permissions list in manifest.json and mirror the same permission
in manifest.dev.json. Update the opt-in assertions in
src/core/utils/__tests__/manifestPermissions.test.ts to verify both ChatGPT
hosts and their corresponding content-script exclusions.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- manifest files ---'
git ls-files '*manifest*.json' '.github/docs/REGRESSION_NOTES.md' | sed -n '1,120p'

printf '%s\n' '--- relevant manifest sections ---'
for f in manifest.json manifest.dev.json; do
  if [ -f "$f" ]; then
    echo "### $f"
    nl -ba "$f" | sed -n '1,100p'
  fi
done

printf '%s\n' '--- all_urls and permission-related references ---'
rg -n --hidden -S '<all_urls>|optional_host_permissions|host_permissions|permissions|contains.*Permission|permission' \
  -g '!node_modules' -g '!dist' -g '!build' . | sed -n '1,300p'

printf '%s\n' '--- ChatGPT/export references ---'
rg -n --hidden -i -S 'chatgpt|export' \
  -g '!node_modules' -g '!dist' -g '!build' . | sed -n '1,300p'

Repository: Nagi-ovo/voyager

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- manifest excerpts ---'
for f in manifest.json manifest.dev.json; do
  echo "### $f"
  awk 'NR >= 20 && NR <= 95 { printf "%5d %s\n", NR, $0 }' "$f"
done

printf '%s\n' '--- current diff for manifests and permission code ---'
git diff -- manifest.json manifest.dev.json src/pages/background/index.ts vite.config.base.ts vite.config.chrome.ts vite.config.firefox.ts vite.config.safari.ts | sed -n '1,260p'

printf '%s\n' '--- generated UI capture implementation ---'
awk 'NR >= 80 && NR <= 120 { printf "%5d %s\n", NR, $0 }
     NR >= 1580 && NR <= 1660 { printf "%5d %s\n", NR, $0 }' src/pages/background/index.ts

printf '%s\n' '--- export call sites ---'
awk 'NR >= 160 && NR <= 220 { printf "%5d %s\n", NR, $0 }' src/pages/content/export/index.ts
rg -n -S 'requestGeneratedUiCapturePermission|GENERATED_UI_CAPTURE_PERMISSION_ORIGINS|capture.*generated|generated.*capture' src/pages src/core | sed -n '1,160p'

printf '%s\n' '--- permission guidance and regression notes ---'
awk 'NR >= 50 && NR <= 68 { printf "%5d %s\n", NR, $0 }' .agents/skills/voyager-contribute/references/browser-testing.md
awk 'NR >= 1 && NR <= 55 { printf "%5d %s\n", NR, $0 }' docs/en/privacy.md
awk 'NR >= 1 && NR <= 30 { printf "%5d %s\n", NR, $0 }' src/pages/content/changelog/notes/1.5.3.md

Repository: Nagi-ovo/voyager

Length of output: 21755


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- generated UI selector and export flow ---'
rg -n -S -C 4 \
  'GENERATED_UI_FRAME_SELECTOR|ensureGeneratedUiScreenshotPermission|captureGeneratedUiScreenshots|GENERATED_UI_CAPTURE_PERMISSION_MESSAGE_TYPE|captureVisibleTab' \
  src/pages/content/export/index.ts src/pages/background/index.ts src/pages/background/runtimeMessageRouting.ts | sed -n '1,300p'

printf '%s\n' '--- background message handler ---'
awk 'NR >= 1740 && NR <= 1810 { printf "%5d %s\n", NR, $0 }' src/pages/background/index.ts

printf '%s\n' '--- export entry points that request the permission ---'
rg -n -S -C 8 \
  'ensureGeneratedUiScreenshotPermission|captureGeneratedUiScreenshots' \
  src/pages/content/export/index.ts | sed -n '1,260p'

printf '%s\n' '--- supported host and activeTab declarations ---'
python3 - <<'PY'
import json
for path in ("manifest.json", "manifest.dev.json"):
    with open(path, encoding="utf-8") as f:
        m = json.load(f)
    print(path)
    print("permissions:", m.get("permissions"))
    print("host_permissions:", m.get("host_permissions"))
    print("optional_host_permissions:", m.get("optional_host_permissions"))
    print("content_script_matches:", [
        match
        for script in m.get("content_scripts", [])
        for match in script.get("matches", [])
    ])
    print("web_accessible_matches:", [
        item.get("matches", [])
        for item in m.get("web_accessible_resources", [])
    ])
PY

Repository: Nagi-ovo/voyager

Length of output: 17467


🌐 Web query:

site:developer.chrome.com/docs/extensions/reference/api/tabs captureVisibleTab activeTab <all_urls> permission

💡 Result:

To use the chrome.tabs.captureVisibleTab method in a Chrome extension, you must possess either the activeTab permission or the <all_urls> host permission [1]. The activeTab permission is often preferred because it grants temporary access to the current tab only in response to a direct user action (such as clicking an extension icon), and it does not trigger the broad permission warnings associated with <all_urls> [1]. Additionally, activeTab allows the capture of sensitive pages—such as chrome:-scheme pages, other extension pages, and data: URLs—which cannot be captured using only <all_urls> [1]. If your extension requires the ability to capture tabs without a direct user interaction, <all_urls> or specific host permissions are required, though these will not allow access to the aforementioned sensitive restricted pages [1]. Note that file URLs can only be captured if the user has explicitly granted the extension file access [1].

Citations:


🌐 Web query:

site:developer.chrome.com/docs/extensions/develop/concepts/activeTab activeTab user gesture content script click captureVisibleTab

💡 Result:

The activeTab permission grants an extension temporary, elevated access to the currently active tab only after an explicit user gesture, such as clicking an extension action, using a context menu item, triggering a keyboard shortcut, or accepting an omnibox suggestion [1]. Regarding your specific query points: Content Scripts: Content scripts cannot directly call chrome.tabs APIs, including captureVisibleTab. Content scripts run in the context of the web page and must communicate with a background service worker or popup via messaging to trigger API calls that require extension-level privileges [1]. CaptureVisibleTab and Permissions: The chrome.tabs.captureVisibleTab API requires the <all_urls> host permission or the activeTab permission. If your extension uses activeTab, you must trigger the capture logic from your background script or popup in response to a user gesture that activates the tab permission [1]. User Gesture Requirement: While the activeTab permission itself is strictly gated by a user gesture, the captureVisibleTab API does not have an additional, separate user gesture requirement beyond the need for the extension to have valid host or activeTab permissions to perform the capture [1]. In summary, to use captureVisibleTab with activeTab, you must ensure the user has initiated an action (like a click) that grants your extension access to the active tab [1]. From there, you can execute the capture command in your background service worker or popup.

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- manifest and code provenance ---'
git status --short
git diff --stat
git log -1 --oneline -- manifest.json manifest.dev.json src/pages/background/index.ts src/pages/content/export/index.ts

printf '%s\n' '--- dynamic content-script registration and export scope ---'
rg -n -S -C 5 \
  'CUSTOM_CONTENT_SCRIPT_ID|registerContentScripts|src/pages/content/index|export' \
  src/pages/background/index.ts src/pages/content/index.tsx src/pages/content/export/index.ts | sed -n '1,320p'

Repository: Nagi-ovo/voyager

Length of output: 22999


Obtain explicit approval before retaining <all_urls>. ensureGeneratedUiCapturePermission() requests it during Gemini generated-UI export, not ChatGPT export. Remove it if the capture flow can use narrower access; otherwise document the requirement and obtain approval in both manifests.

📍 Affects 2 files
  • manifest.json#L44-L44 (this comment)
  • manifest.dev.json#L58-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@manifest.json` at line 44, Remove <all_urls> from optional_host_permissions
in both manifest.json (line 44) and manifest.dev.json (line 58) if
ensureGeneratedUiCapturePermission() supports narrower access; otherwise
document the requirement and obtain explicit approval for retaining it in both
manifests.

Source: Coding guidelines

"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'; worker-src 'self'"
},
Expand Down
5 changes: 5 additions & 0 deletions public/contentStyle.css
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,11 @@ body.dark-theme .gv-export-dropdown-btn:hover,
pointer-events: auto;
}

.gv-persistent-export-toolbar[data-gv-platform='chatgpt'] {
top: 50px;
right: calc(var(--gv-persistent-export-right, 84px) - 50px);
}

.gv-persistent-export-btn {
display: inline-flex;
align-items: center;
Expand Down
16 changes: 15 additions & 1 deletion src/core/utils/__tests__/manifestPermissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,21 @@ afterAll(() => {
describe('manifest permissions', () => {
it('keeps all-site access optional', () => {
expect(manifestChrome.host_permissions).not.toContain('<all_urls>');
expect(manifestChrome.optional_host_permissions).toEqual(['<all_urls>']);
expect(manifestChrome.optional_host_permissions).toEqual(
expect.arrayContaining(['<all_urls>']),
);
Comment on lines +77 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Assert the complete optional-host allowlist.

expect.arrayContaining(['<all_urls>']) verifies only one entry and permits unexpected host permissions. This weakens the security regression test. Assert the complete approved set so accidental permission broadening fails the test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/utils/__tests__/manifestPermissions.test.ts` around lines 77 - 79,
Update the optional_host_permissions assertion in the manifest permissions test
to compare against the complete approved allowlist, rather than using
expect.arrayContaining with only <all_urls>. Include every expected host
permission and ensure unexpected entries cause the test to fail.

});

it('keeps ChatGPT host access opt-in', () => {
expect(manifestChrome.host_permissions).not.toContain('https://chatgpt.com/*');
expect(manifestChrome.optional_host_permissions).toEqual(
expect.arrayContaining(['https://chatgpt.com/*']),
);
expect(
manifestChrome.content_scripts.some((entry) =>
entry.matches.includes('https://chatgpt.com/*'),
),
).toBe(false);
});

it('keeps unlimitedStorage out of the shared manifest', () => {
Expand Down
Loading
Loading