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
9 changes: 2 additions & 7 deletions config/schema/ckeditor_ai_agent.ckeditor5.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,9 @@ ckeditor5.plugin.ckeditor_ai_agent_ai_agent:
type: mapping
label: 'AI Output Security'
mapping:
allowedImageDomains:
allowedDomains:
type: sequence
label: 'Allowed Image Domains'
sequence:
type: string
allowedLinkDomains:
type: sequence
label: 'Allowed Link Domains'
label: 'Allowed Domains'
sequence:
type: string
toneOfVoiceVocabulary:
Expand Down
9 changes: 2 additions & 7 deletions config/schema/ckeditor_ai_agent.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,9 @@ ckeditor_ai_agent.settings:
type: mapping
label: 'AI Output Security'
mapping:
allowedImageDomains:
allowedDomains:
type: sequence
label: 'Allowed Image Domains'
sequence:
type: string
allowedLinkDomains:
type: sequence
label: 'Allowed Link Domains'
label: 'Allowed Domains'
sequence:
type: string

Expand Down
2 changes: 1 addition & 1 deletion js/build/ai-agent.js

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions js/ckeditor5_plugins/aiagent/src/SUPPORTED_MODELS.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
"kavya-m1-eu"
],
"google": [
"gemini-2.5-pro-preview-05-06",
"gemini-2.5-pro-preview-03-25",
"gemini-2.5-pro-preview-06-05",
"gemini-2.5-pro",
"learnlm-2.0-flash-experimental",
"gemma-3n-e4b-it",
Expand All @@ -23,7 +20,6 @@
"gemini-2.5-flash-lite-preview-09-2025",
"gemini-2.5-flash-lite",
"gemini-2.5-flash-preview-09-2025",
"gemini-2.0-flash-thinking-exp-1219",
"gemini-2.5-flash",
"gemini-2.0-pro-exp-02-05",
"gemini-2.0-pro-exp",
Expand Down Expand Up @@ -110,9 +106,13 @@
"devstral-small-latest",
"magistral-medium-latest",
"magistral-small-latest",
"ministral-14b-latest",
"ministral-3b-latest",
"ministral-3b-latest",
"ministral-8b-latest",
"ministral-8b-latest",
"mistral-large-2411",
"mistral-large-2512",
"mistral-large-latest",
"mistral-medium-2505",
"mistral-medium-latest",
Expand Down Expand Up @@ -159,9 +159,11 @@
"aion-labs/aion-rp-llama-3.1-8b",
"alfredpros/codellama-7b-instruct-solidity",
"allenai/olmo-2-0325-32b-instruct",
"allenai/olmo-3-32b-think",
"allenai/olmo-3-32b-think:free",
"allenai/olmo-3-7b-instruct",
"allenai/olmo-3-7b-think",
"amazon/nova-2-lite-v1",
"amazon/nova-2-lite-v1:free",
"amazon/nova-lite-v1",
"amazon/nova-micro-v1",
"amazon/nova-premier-v1",
Expand Down Expand Up @@ -192,7 +194,6 @@
"baidu/ernie-4.5-300b-a47b",
"baidu/ernie-4.5-vl-28b-a3b",
"baidu/ernie-4.5-vl-424b-a47b",
"openrouter/bert-nebulon-alpha",
"bytedance/ui-tars-1.5-7b",
"deepcogito/cogito-v2-preview-llama-109b-moe",
"cohere/command-a",
Expand Down Expand Up @@ -304,6 +305,7 @@
"mistralai/mistral-7b-instruct-v0.1",
"mistralai/mistral-7b-instruct-v0.2",
"mistralai/mistral-7b-instruct-v0.3",
"mistralai/mistral-large-2512",
"mistralai/mistral-medium-3",
"mistralai/mistral-medium-3.1",
"mistralai/mistral-nemo",
Expand Down
2 changes: 1 addition & 1 deletion js/ckeditor5_plugins/aiagent/src/aiagentservice.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class AiAgentService {
this.htmlParser = new HtmlParser(editor);
// Wire up blocked URL notifications to the UI component
const filterConfig = {
...config.aiOutputSecurity,
allowedDomains: config.aiOutputSecurity?.allowedDomains,
onUrlBlocked: (blockedUrls) => {
const uiComponent = aiAgentContext.uiComponent;
if (uiComponent?.showBlockedUrlsWarning) {
Expand Down
7 changes: 2 additions & 5 deletions js/ckeditor5_plugins/aiagent/src/aiagentui.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,9 @@ export default class AiAgentUI extends Plugin {
/**
* Displays a warning notification for blocked URLs.
*
* @param blockedUrls - Object containing arrays of blocked image and link URLs.
* @param blockedUrls - Array of blocked URL strings.
*/
showBlockedUrlsWarning(blockedUrls: {
images: string[];
links: string[];
}): void;
showBlockedUrlsWarning(blockedUrls: string[]): void;
/**
* Escapes HTML special characters to prevent XSS.
*/
Expand Down
22 changes: 6 additions & 16 deletions js/ckeditor5_plugins/aiagent/src/aiagentui.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,25 +339,14 @@ export default class AiAgentUI extends Plugin {
/**
* Displays a warning notification for blocked URLs.
*
* @param blockedUrls - Object containing arrays of blocked image and link URLs.
* @param blockedUrls - Array of blocked URL strings.
*/
showBlockedUrlsWarning(blockedUrls) {
const totalBlocked = blockedUrls.images.length + blockedUrls.links.length;
if (totalBlocked === 0)
if (blockedUrls.length === 0)
return;
const t = this.editor.t;
const parts = [];
if (blockedUrls.images.length > 0) {
const imageWord = blockedUrls.images.length === 1 ? t('image') : t('images');
parts.push(`${blockedUrls.images.length} ${imageWord}`);
}
if (blockedUrls.links.length > 0) {
const linkWord = blockedUrls.links.length === 1 ? t('link') : t('links');
parts.push(`${blockedUrls.links.length} ${linkWord}`);
}
const allUrls = [...blockedUrls.images, ...blockedUrls.links];
const displayUrls = allUrls.slice(0, MAX_BLOCKED_URLS_DISPLAYED);
const remainingCount = allUrls.length - displayUrls.length;
const displayUrls = blockedUrls.slice(0, MAX_BLOCKED_URLS_DISPLAYED);
const remainingCount = blockedUrls.length - displayUrls.length;
const urlListItems = displayUrls.map(url => {
const truncated = url.length > MAX_URL_DISPLAY_LENGTH
? `${url.substring(0, MAX_URL_DISPLAY_LENGTH)}...`
Expand All @@ -367,8 +356,9 @@ export default class AiAgentUI extends Plugin {
if (remainingCount > 0) {
urlListItems.push(`<li>...${t('and %0 more', [remainingCount])}</li>`);
}
const urlWord = blockedUrls.length === 1 ? t('URL') : t('URLs');
const message = `<strong>${t('External URLs filtered')}</strong><br>` +
`${parts.join(` ${t('and')} `)} ${t('blocked for security.')}<br>` +
`${blockedUrls.length} ${urlWord} ${t('blocked for security.')}<br>` +
`<ul class="blocked-urls-list">${urlListItems.join('')}</ul>`;
this.showGptErrorToolTip(message, { type: 'warning', html: true, duration: BLOCKED_URL_WARNING_DURATION });
}
Expand Down
10 changes: 3 additions & 7 deletions js/ckeditor5_plugins/aiagent/src/type-identifiers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,11 @@ export interface AiAgentConfig {
moderationDisableFlags?: Array<ModerationFlagsTypes>;
aiOutputSecurity?: {
/**
* Allowed domains for images. Supports wildcards (*.example.com).
* Allowed domains for external URLs (images and links).
* Supports wildcards (*.example.com).
* Default: ['promptahuman.com']. Use [] to block all, ['*'] to allow all.
*/
allowedImageDomains?: string[];
/**
* Allowed domains for links. Supports wildcards (*.example.com).
* Default: [] (blocks all). Use ['*'] to allow all external links.
*/
allowedLinkDomains?: string[];
allowedDomains?: string[];
};
commandsDropdown?: Array<{
title: string;
Expand Down
31 changes: 4 additions & 27 deletions js/ckeditor5_plugins/aiagent/src/util/ai-output-filter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,13 @@
* Mitigates prompt injection attacks that attempt to exfiltrate data via
* malicious URLs in AI-generated content (CVE-2025-32711 / EchoLeak style attacks).
*
* Filters:
* - Images: <img> tags and Markdown ![alt](url) syntax including reference-style (enabled by default)
* - Links: <a> tags and Markdown [text](url) syntax including reference-style (enabled by default)
* - Plain text URLs: Bare http(s):// URLs in text content (replaced with EXTERNAL_URL_REDACTED)
* - Iframes: All <iframe> tags are unconditionally removed (always enabled)
* - Dangerous elements: <object>, <embed>, <applet>, SVG <image> (always enabled)
* Uses UNIVERSAL URL detection - scans ALL content for URLs regardless of context.
* No more whack-a-mole with specific tags/attributes.
*
* @see https://nvd.nist.gov/vuln/detail/CVE-2025-32711
*/
export interface AiFilterConfig {
/**
* Allowed domains for images. Supports wildcards (*.example.com).
* Default: ['promptahuman.com']. Use [] to block all, ['*'] to allow all.
*/
allowedImageDomains?: string[];
/**
* Allowed domains for links. Supports wildcards (*.example.com).
* Default: [] (blocks all). Use ['*'] to allow all external links.
*/
allowedLinkDomains?: string[];
/** Callback to notify about blocked URLs. Called with blocked URL info after filtering. */
onUrlBlocked?: (blockedUrls: BlockedUrlInfo) => void;
allowedDomains?: string[];
onUrlBlocked?: (blockedUrls: string[]) => void;
}
export interface BlockedUrlInfo {
images: string[];
links: string[];
}
/**
* Main filter function for AI-generated content.
* Automatically detects HTML vs Markdown content.
* Calls onUrlBlocked callback if any URLs were blocked.
*/
export declare const filterAiImages: (content: string, config?: AiFilterConfig) => string;
Loading