Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions browser/components/smartwindow/content/smartbar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
getMentionSuggestions,
} from "chrome://browser/content/smartwindow/mentions.mjs";

import { seedMentionedUrl } from "./utils.mjs";

// Track autofill state
let autofillState = null;
let deletedQuery = "";
Expand Down Expand Up @@ -252,6 +254,12 @@ export function attachToElement(element, options = {}) {
dropdown = new MentionDropdown();
currentCommand = props.command;
dropdown.create(props.items, item => {
// Seed the @mentioned URL into the security ledger
// This grants the AI permission to access this URL
if (item.id) {
seedMentionedUrl(item.id);
}

currentCommand({
id: item.id,
label: item.label,
Expand Down
26 changes: 19 additions & 7 deletions browser/components/smartwindow/content/smartwindow.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { detectQueryType, searchBrowserHistory } from "./utils.mjs";
import { detectQueryType, searchBrowserHistory, setSecurityOrchestrator } from "./utils.mjs";
import { attachToElement } from "chrome://browser/content/smartwindow/smartbar.mjs";
import {
generateLiveSuggestions,
Expand All @@ -14,7 +14,6 @@ import {
CHAT_HISTORY_CONVERSATION_SELECTED_EVENT,
} from "chrome://browser/content/smartwindow/chat-history.mjs";
import { deleteInsight, getInsightSummariesForPrompt } from "./insights.mjs";
import { generateId } from "chrome://global/content/ml/security/SecurityUtils.sys.mjs";

const { ChatHistory, ChatHistoryConversation } = ChromeUtils.importESModule(
"resource:///modules/smartwindow/ChatHistory.sys.mjs"
Expand All @@ -35,6 +34,9 @@ const { TabStateFlusher } = ChromeUtils.importESModule(
const { SecurityOrchestrator } = ChromeUtils.importESModule(
"chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs"
);
const { generateId } = ChromeUtils.importESModule(
"chrome://global/content/ml/security/SecurityUtils.sys.mjs"
);
const { embedderElement, topChromeWindow } = window.browsingContext;
const gBrowser = topChromeWindow.gBrowser;

Expand Down Expand Up @@ -119,18 +121,25 @@ class SmartWindowPage {
// Only initialize if Smart Window security is enabled
const sessionId = generateId("smart-window");
const securityEnabled = Services.prefs.getBoolPref("browser.smartwindow.security.enabled", true);

this.securityOrchestrator = null;

if (securityEnabled) {
SecurityOrchestrator.init(sessionId).then(sessionLedger => {
this.sessionLedger = sessionLedger;
console.log("[Security] Smart Window security enabled - orchestrator initialized");
SecurityOrchestrator.create(sessionId).then(orchestrator => {
this.securityOrchestrator = orchestrator;
this.sessionLedger = orchestrator.getSessionLedger();
setSecurityOrchestrator(orchestrator);
console.warn("[Security] AI Window security enabled - orchestrator initialized");
}).catch(error => {
console.error("[Security] Failed to initialize orchestrator:", error);
this.securityOrchestrator = null;
this.sessionLedger = null;
setSecurityOrchestrator(null);
});
} else {
this.sessionLedger = null;
console.log("[Security] Smart Window security DISABLED via kill switch - running in pass-through mode");
setSecurityOrchestrator(null);
console.warn("[Security] AI Window security DISABLED via kill switch - running in pass-through mode");
}

gBrowser.selectedTab.conversation = new ChatHistoryConversation({
Expand Down Expand Up @@ -180,7 +189,10 @@ class SmartWindowPage {
);
}

SecurityOrchestrator.reset();
if (this.securityOrchestrator) {
this.securityOrchestrator.reset();
setSecurityOrchestrator(null);
}
}

getQueryTypeIcon(type) {
Expand Down
78 changes: 64 additions & 14 deletions browser/components/smartwindow/content/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,59 @@ ChromeUtils.defineESModuleGetters(lazy, {
PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs",
PageThumbsStorage: "resource://gre/modules/PageThumbs.sys.mjs",
getPlacesSemanticHistoryManager: "resource://gre/modules/PlacesSemanticHistoryManager.sys.mjs",
SecurityOrchestrator:
"chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs",
});

/**
* Module-level security orchestrator instance.
* Set by SmartWindowPage after initialization.
*
* @type {import("chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs").SecurityOrchestrator|null}
*/
let securityOrchestrator = null;

/**
* Sets the security orchestrator instance for this window's utils module.
* Called by SmartWindowPage after creating the orchestrator.
*
* @param {import("chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs").SecurityOrchestrator|null} orchestrator
*/
export function setSecurityOrchestrator(orchestrator) {
securityOrchestrator = orchestrator;
}

/**
* Seeds a URL into the session ledger for the current tab.
* Call this when a user explicitly @mentions a tab/URL.
* This grants the AI permission to access the URL's content.
*
* @param {string} url - The URL to seed
*/
export function seedMentionedUrl(url) {
if (!securityOrchestrator) {
console.log("[Security] No orchestrator - skipping mention seeding");
return;
}

const sessionLedger = securityOrchestrator.getSessionLedger();
if (!sessionLedger) {
console.log("[Security] No session ledger - skipping mention seeding");
return;
}

const win = lazy.BrowserWindowTracker.getTopWindow();
const gBrowser = win.gBrowser;
const tabId = gBrowser.selectedTab.linkedPanel;

const tabLedger = sessionLedger.forTab(tabId);
const added = tabLedger.add(url);

if (added) {
console.log(`[Security] Seeded @mentioned URL: ${url} for tab ${tabId}`);
} else {
console.warn(`[Security] Failed to seed @mentioned URL: ${url} for tab ${tabId}`);
Copy link

@noahpodgurski noahpodgurski Dec 2, 2025

Choose a reason for hiding this comment

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

question: Would it make sense for this to be considered an error and brought to the user's attention that the permission access request failed?

Copy link
Author

Choose a reason for hiding this comment

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

Great question! I've given this some thought. Since these log messages are intended for developers rather than users, I don't think we need to surface this to the user directly. They'll see a denial when the tool call is evaluated by the security layer (fail-closed behavior).

However, I've tightened up the logging to give better signal: "no ledger available" is now console.error, while "already in ledger" is console.debug. This should give developers better visibility into the unexpected failures.

}
}

import { createEngine } from "chrome://global/content/ml/EngineProcess.sys.mjs";
import { SmartAssistEngine } from "moz-src:///browser/components/genai/SmartAssistEngine.sys.mjs";
import { deleteInsight, findRelatedInsight, generateInsightsFromDirectChat } from "chrome://browser/content/smartwindow/insights.mjs";
Expand Down Expand Up @@ -598,7 +647,7 @@ async function checkToolSecurity(toolName, toolParams, requestId) {
await browser.browsingContext.currentWindowContext.getActor(
"SmartWindowMeta"
);
const sessionLedger = lazy.SecurityOrchestrator.getSessionLedger();
const sessionLedger = securityOrchestrator?.getSessionLedger();

if (!sessionLedger) {
console.log(
Expand Down Expand Up @@ -633,7 +682,7 @@ async function checkToolSecurity(toolName, toolParams, requestId) {
tabId = gBrowser.selectedTab.linkedPanel;
}

const decision = await lazy.SecurityOrchestrator.evaluate({
const decision = await securityOrchestrator.evaluate({
phase: "tool.execution",
action: {
type: "tool.call",
Expand Down Expand Up @@ -955,12 +1004,12 @@ export async function* fetchWithHistory(messages, allowedUrls) {
error: securityCheck.reason || "Security policy denied this action",
};
} else {
// Populate allowedUrls from current tab's ledger for headless extraction
// Merge session ledger URLs with any pre-existing allowedUrls for headless extraction.
// This allows fetching URLs that are part of the current page's metadata
// Such as (canonical URLs, related links, etc.) even if they're not in open tabs
let allowedUrls = new Set();
// (canonical URLs, related links, etc.) even if they're not in open tabs.
let mergedAllowedUrls = new Set(allowedUrls || []);
try {
const sessionLedger = lazy.SecurityOrchestrator.getSessionLedger();
const sessionLedger = securityOrchestrator?.getSessionLedger();

if (sessionLedger) {
// Get current tab ID from tool params or browser
Expand All @@ -985,27 +1034,28 @@ export async function* fetchWithHistory(messages, allowedUrls) {
if (currentTabId) {
const tabLedger = sessionLedger.forTab(currentTabId);
if (tabLedger) {
allowedUrls = new Set(tabLedger.getAll());
console.log(
`[Security] Allowing headless extraction for ${allowedUrls.size} URLs from current tab ${currentTabId}`
for (const url of tabLedger.getAll()) {
mergedAllowedUrls.add(url);
}
console.warn(
`[Security] Allowing headless extraction for ${mergedAllowedUrls.size} URLs from current tab ${currentTabId}`
);
}
}
}
} catch (error) {
console.warn(
"[Security] Could not populate allowedUrls for headless extraction:",
"[Security] Could not populate mergedAllowedUrls for headless extraction:",
error
);
allowedUrls = new Set();
}

switch (toolName) {
case SEARCH_OPEN_TABS:
result = search_open_tabs(toolParams);
break;
case GET_PAGE_CONTENT:
result = await get_page_content(toolParams, allowedUrls);
result = await get_page_content(toolParams, mergedAllowedUrls);
break;
case SEARCH_HISTORY:
result = await searchBrowserHistory(toolParams);
Expand Down
1 change: 0 additions & 1 deletion toolkit/components/ml/jar.mn
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ toolkit.jar:
content/global/ml/security/SecurityLogger.sys.mjs (security/SecurityLogger.sys.mjs)
content/global/ml/security/SecurityOrchestrator.sys.mjs (security/SecurityOrchestrator.sys.mjs)
content/global/ml/security/SecurityUtils.sys.mjs (security/SecurityUtils.sys.mjs)
content/global/ml/security/SmartWindowIntegration.sys.mjs (security/SmartWindowIntegration.sys.mjs)
content/global/ml/security/policies/tool-execution-policies.json (security/policies/tool-execution-policies.json)
#ifdef NIGHTLY_BUILD
content/global/ml/ort.webgpu-dev.mjs (vendor/ort.webgpu-dev.mjs)
Expand Down
7 changes: 6 additions & 1 deletion toolkit/components/ml/security/PolicyEvaluator.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ export class PolicyEvaluator {
* @returns {boolean} True if policy applies to this action
*/
static checkMatch(matchCriteria, action) {
console.warn("[PolicyEvaluator] checkMatch criteria:", JSON.stringify(matchCriteria), "action:", JSON.stringify(action));
console.warn(

Choose a reason for hiding this comment

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

why warn here? shouln't it be debug?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, that makes sense. I will change this to debug 🐛

Copy link
Author

Choose a reason for hiding this comment

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

Ah, right, I remembered why I changed this to warn: the linter doesn't like this :( When I run ./mach lint, I get the following lint error:

  34:5  error  Unexpected console statement. Only these console methods are allowed: createInstance, error, warn.  no-console (eslint)

For now, I can add the comment to disable the lint error since this is a log intentionally for debugging purposes.

"[PolicyEvaluator] checkMatch criteria:",
JSON.stringify(matchCriteria),
"action:",
JSON.stringify(action)
);
if (!matchCriteria || typeof matchCriteria !== "object") {
return false;
}
Expand Down
Loading