Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/**
* Type definitions and helpers for the Smart Window security layer.
* Defines SecurityDecision, DenialCodes, and allow/deny helper functions.
*/
/**
* Security decision allowing an action to proceed.
*
* @typedef {object} SecurityDecisionAllow
* @property {"allow"} effect - The decision effect
*/
/**
* Security decision denying an action with structured error information.
*
* @typedef {object} SecurityDecisionDeny
* @property {"deny"} effect - The decision effect
* @property {string} policyId - The policy that made this decision (e.g., "block-unseen-links")
* @property {string} code - Denial code (see DenialCodes)
* @property {string} reason - Explanation of the denial
* @property {object} [details] - Optional additional context for logging/debugging
*/
/**
* Result of policy evaluation.
* Either allows the action or denies it with structured error information.
*
* @typedef {SecurityDecisionAllow | SecurityDecisionDeny} SecurityDecision
*/
/**
* An action being evaluated by the security layer.
*
* Represents a request to perform an operation (e.g., tool call) that
* requires security validation.
*
* @typedef {object} SecurityAction
* @property {"tool.call"} type - Action type (extensible for future action types)
* @property {string} tool - Tool name (case-sensitive, matches dispatcher constants)
* @property {string[]} [urls] - URLs being accessed by the tool (always array, may be empty)
* @property {string} tabId - The originating tab ID for this action
* @property {object} [args] - Original tool arguments (for logging/debugging)
*/
/**
* Request-scoped context for policy evaluation.
*
* Security Note:
* Context is rebuilt for each request and discarded afterward to prevent
* cross-request authorization leakage.
*
* @typedef {object} SecurityContext
* @property {TabLedger} linkLedger - Request-scoped link ledger (union of authorized sources)
* @property {string} sessionId - Smart Window session identifier
* @property {string} requestId - Individual request identifier (for logging/correlation)
* @property {string} currentTabId - The active/focused tab
* @property {string[]} [mentionedTabIds] - Tab IDs explicitly referenced via @mentions (future)
*/
/**
* Structured error thrown when a security policy denies an action.
*
* This error allows the tool dispatcher to catch and handle policy denials
* gracefully, distinguishing them from other errors (e.g., network failures).
*/
export class SecurityPolicyError extends Error {
/**
* Creates a structured error from a denial decision.
*
* @param {SecurityDecisionDeny} decision - The denial decision
*/
constructor(decision) {
super(decision.reason);
this.name = "SecurityPolicyError";
this.code = decision.code;
this.policyId = decision.policyId;
this.decision = decision;
}
/**
* Serializes the error for structured logging.
* Avoids circular references and provides stable JSON output.
*
* @returns {object} Structured representation of the error
*/
toJSON() {
return {
name: this.name,
code: this.code,
policyId: this.policyId,
message: this.message,
decision: this.decision,
};
}
}
/**
* @typedef {'allow' | 'deny'} PolicyEffect
*/
/** @type {PolicyEffect} */
export const EFFECT_ALLOW = "allow";
/** @type {PolicyEffect} */
export const EFFECT_DENY = "deny";
// Standard denial codes for consistent error handling across the security layer.
export const DenialCodes = Object.freeze({
// URL not present in the request-scoped link ledger.
// e.g., "block-unseen-links" policy
UNSEEN_LINK: "UNSEEN_LINK",
// URL parsing or normalization failed.
// Fail-closed behavior: treat malformed URLs as untrusted.
MALFORMED_URL: "MALFORMED_URL",
// Required context (e.g., link ledger, tab ID) not provided.
// Fail-closed behavior: cannot evaluate without proper context.
MISSING_CONTEXT: "MISSING_CONTEXT",
// Policy enforcement is disabled (from policy configuration file).
POLICY_DISABLED: "POLICY_DISABLED",
});
// Standard reason phrases for denial codes.
export const ReasonPhrases = Object.freeze({
UNSEEN_LINK: "URL not in selected request context",
MALFORMED_URL: "Failed to parse or normalize URL",
MISSING_CONTEXT: "Missing required evaluation context",
POLICY_DISABLED: "Policy enforcement disabled",
});
/**
* Creates an "allow" decision.
*
* @returns {SecurityDecisionAllow} Allow decision
*/
export const createAllowDecision = () =>
/** @type {SecurityDecisionAllow} */ ({
effect: EFFECT_ALLOW,
});
/**
* Creates a "deny" decision with structured error information.
*
* @param {string} code - Denial code from DenialCodes
* @param {string} reason - Reason phrase for denial (from ReasonPhrases)
* @param {object} [details] - Optional additional context
* @param {string} [policyId="block-unseen-links"] - The policy making this decision
* @returns {SecurityDecisionDeny} Deny decision
*/
export const createDenyDecision = (
code,
reason,
details = undefined,
policyId = "block-unseen-links"
) =>
/** @type {SecurityDecisionDeny} */ ({
effect: EFFECT_DENY,
policyId,
code,
reason,
details,
});
/**
* Type guard: checks if a decision is an allow.
*
* @param {SecurityDecision | undefined | null} decision - Decision to check
* @returns {boolean} True if decision is allow
*/
export const isAllowDecision = decision => decision?.effect === EFFECT_ALLOW;
/**
* Type guard: checks if a decision is a deny.
*
* @param {SecurityDecision | undefined | null} decision - Decision to check
* @returns {boolean} True if decision is deny
*/
export const isDenyDecision = decision => decision?.effect === EFFECT_DENY;