secubox-openwrt/package/secubox/luci-app-ollama/htdocs/luci-static/resources/view/ollama/chat.js
CyberMind-FR e58f479cd4 feat(waf): Update WAF scenarios with 2024-2025 CVEs and OWASP threats
Add detection patterns for latest actively exploited vulnerabilities:
- CVE-2025-55182 (React2Shell, CVSS 10.0)
- CVE-2025-8110 (Gogs RCE), CVE-2025-53770 (SharePoint)
- CVE-2025-52691 (SmarterMail), CVE-2025-40551 (SolarWinds)
- CVE-2024-47575 (FortiManager), CVE-2024-21887 (Ivanti)
- CVE-2024-3400, CVE-2024-0012, CVE-2024-9474 (PAN-OS)

New attack categories based on OWASP Top 10 2025:
- HTTP Request Smuggling (TE.CL/CL.TE conflicts)
- AI/LLM Prompt Injection (ChatML, instruction markers)
- WAF Bypass techniques (Unicode normalization, double encoding)
- Supply Chain attacks (CI/CD poisoning, dependency confusion)
- Extended SSTI (Jinja2, Freemarker, Velocity, Thymeleaf)
- API Abuse (BOLA/IDOR, mass assignment)

CrowdSec scenarios split into 11 separate files for reliability.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 05:02:57 +01:00

399 lines
10 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
'require view';
'require ui';
'require rpc';
'require secubox/kiss-theme';
var callModels = rpc.declare({
object: 'luci.ollama',
method: 'models',
expect: { models: [] }
});
var callChat = rpc.declare({
object: 'luci.ollama',
method: 'chat',
params: ['model', 'message'],
expect: { response: '' }
});
var callStatus = rpc.declare({
object: 'luci.ollama',
method: 'status',
expect: { running: false }
});
return view.extend({
title: _('Ollama Chat'),
chatHistory: [],
selectedModel: null,
load: function() {
return Promise.all([
callModels(),
callStatus()
]).then(function(results) {
return {
models: Array.isArray(results[0]) ? results[0] : [],
status: results[1] || {}
};
});
},
render: function(data) {
var self = this;
var models = data.models;
var status = data.status;
if (!status.running) {
return E('div', { 'class': 'ollama-chat' }, [
E('style', {}, this.getCSS()),
E('div', { 'class': 'oll-chat-offline' }, [
E('span', { 'class': 'oll-offline-icon' }, '⚠️'),
E('h3', {}, _('Ollama is not running')),
E('p', {}, _('Start the service to use chat')),
E('code', {}, '/etc/init.d/ollama start')
])
]);
}
if (models.length === 0) {
return E('div', { 'class': 'ollama-chat' }, [
E('style', {}, this.getCSS()),
E('div', { 'class': 'oll-chat-offline' }, [
E('span', { 'class': 'oll-offline-icon' }, '📦'),
E('h3', {}, _('No models available')),
E('p', {}, _('Download a model first')),
E('code', {}, 'ollamactl pull tinyllama')
])
]);
}
this.selectedModel = models[0].name;
var container = E('div', { 'class': 'ollama-chat' }, [
E('style', {}, this.getCSS()),
// Header
E('div', { 'class': 'oll-chat-header' }, [
E('div', { 'class': 'oll-chat-title' }, [
E('span', { 'class': 'oll-chat-icon' }, '🦙'),
E('span', {}, _('Ollama Chat'))
]),
E('div', { 'class': 'oll-model-select-wrapper' }, [
E('label', {}, _('Model:')),
E('select', {
'class': 'oll-model-select',
'id': 'model-select',
'change': function(ev) { self.selectedModel = ev.target.value; }
}, models.map(function(m) {
return E('option', { 'value': m.name }, m.name);
}))
])
]),
// Chat Messages
E('div', { 'class': 'oll-chat-messages', 'id': 'chat-messages' }, [
E('div', { 'class': 'oll-chat-welcome' }, [
E('span', { 'class': 'oll-welcome-icon' }, '👋'),
E('h3', {}, _('Welcome to Ollama Chat')),
E('p', {}, _('Select a model and start chatting. Your conversation is processed locally.'))
])
]),
// Input Area
E('div', { 'class': 'oll-chat-input-area' }, [
E('textarea', {
'class': 'oll-chat-input',
'id': 'chat-input',
'placeholder': _('Type your message...'),
'rows': 3,
'keydown': function(ev) {
if (ev.key === 'Enter' && !ev.shiftKey) {
ev.preventDefault();
self.sendMessage();
}
}
}),
E('button', {
'class': 'oll-send-btn',
'id': 'send-btn',
'click': function() { self.sendMessage(); }
}, [E('span', {}, ''), _('Send')])
])
]);
return KissTheme.wrap([container], 'admin/services/ollama/chat');
},
sendMessage: function() {
var self = this;
var input = document.getElementById('chat-input');
var message = input.value.trim();
if (!message) return;
var messagesContainer = document.getElementById('chat-messages');
var sendBtn = document.getElementById('send-btn');
// Clear welcome message if present
var welcome = messagesContainer.querySelector('.oll-chat-welcome');
if (welcome) welcome.remove();
// Add user message
this.addMessage('user', message);
input.value = '';
sendBtn.disabled = true;
// Add typing indicator
var typingId = 'typing-' + Date.now();
this.addTypingIndicator(typingId);
// Send to API
callChat(this.selectedModel, message).then(function(result) {
self.removeTypingIndicator(typingId);
sendBtn.disabled = false;
if (result.error) {
self.addMessage('error', result.error);
} else if (result.response) {
self.addMessage('assistant', result.response);
} else {
self.addMessage('error', _('No response received'));
}
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}).catch(function(err) {
self.removeTypingIndicator(typingId);
sendBtn.disabled = false;
self.addMessage('error', err.message);
});
},
addMessage: function(role, content) {
var messagesContainer = document.getElementById('chat-messages');
var msgClass = 'oll-message oll-message-' + role;
var icon = role === 'user' ? '👤' : (role === 'error' ? '' : '🦙');
var msg = E('div', { 'class': msgClass }, [
E('div', { 'class': 'oll-message-icon' }, icon),
E('div', { 'class': 'oll-message-content' }, content)
]);
messagesContainer.appendChild(msg);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
},
addTypingIndicator: function(id) {
var messagesContainer = document.getElementById('chat-messages');
var typing = E('div', { 'class': 'oll-message oll-message-assistant oll-typing', 'id': id }, [
E('div', { 'class': 'oll-message-icon' }, '🦙'),
E('div', { 'class': 'oll-message-content' }, [
E('div', { 'class': 'oll-typing-dots' }, [
E('span', {}), E('span', {}), E('span', {})
])
])
]);
messagesContainer.appendChild(typing);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
},
removeTypingIndicator: function(id) {
var el = document.getElementById(id);
if (el) el.remove();
},
getCSS: function() {
return `
.ollama-chat {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: #030712;
color: #f8fafc;
height: calc(100vh - 100px);
display: flex;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
}
.oll-chat-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
background: #0f172a;
border-bottom: 1px solid #334155;
}
.oll-chat-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 18px;
font-weight: 600;
}
.oll-chat-icon { font-size: 24px; }
.oll-model-select-wrapper {
display: flex;
align-items: center;
gap: 10px;
}
.oll-model-select-wrapper label {
color: #94a3b8;
font-size: 13px;
}
.oll-model-select {
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
padding: 8px 12px;
color: #f8fafc;
font-size: 13px;
cursor: pointer;
}
.oll-chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.oll-chat-welcome {
text-align: center;
padding: 60px 20px;
color: #64748b;
}
.oll-welcome-icon { font-size: 48px; display: block; margin-bottom: 16px; }
.oll-chat-welcome h3 { margin: 0 0 8px; color: #f8fafc; }
.oll-chat-welcome p { margin: 0; }
.oll-message {
display: flex;
gap: 12px;
max-width: 85%;
}
.oll-message-user {
align-self: flex-end;
flex-direction: row-reverse;
}
.oll-message-assistant {
align-self: flex-start;
}
.oll-message-error {
align-self: center;
}
.oll-message-icon {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.oll-message-user .oll-message-icon {
background: linear-gradient(135deg, #3b82f6, #2563eb);
}
.oll-message-assistant .oll-message-icon {
background: linear-gradient(135deg, #f97316, #ea580c);
}
.oll-message-error .oll-message-icon {
background: linear-gradient(135deg, #ef4444, #dc2626);
}
.oll-message-content {
background: #1e293b;
padding: 12px 16px;
border-radius: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.oll-message-user .oll-message-content {
background: linear-gradient(135deg, #3b82f6, #2563eb);
border-radius: 12px 12px 4px 12px;
}
.oll-message-assistant .oll-message-content {
border-radius: 12px 12px 12px 4px;
}
.oll-message-error .oll-message-content {
background: rgba(239, 68, 68, 0.15);
color: #ef4444;
border: 1px solid rgba(239, 68, 68, 0.3);
}
.oll-typing-dots {
display: flex;
gap: 4px;
}
.oll-typing-dots span {
width: 8px;
height: 8px;
background: #64748b;
border-radius: 50%;
animation: typing 1.4s infinite;
}
.oll-typing-dots span:nth-child(2) { animation-delay: 0.2s; }
.oll-typing-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes typing {
0%, 60%, 100% { opacity: 0.3; transform: scale(0.8); }
30% { opacity: 1; transform: scale(1); }
}
.oll-chat-input-area {
padding: 16px 20px;
background: #0f172a;
border-top: 1px solid #334155;
display: flex;
gap: 12px;
}
.oll-chat-input {
flex: 1;
background: #1e293b;
border: 1px solid #334155;
border-radius: 12px;
padding: 12px 16px;
color: #f8fafc;
font-size: 14px;
resize: none;
font-family: inherit;
}
.oll-chat-input:focus {
outline: none;
border-color: #f97316;
}
.oll-send-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
background: linear-gradient(135deg, #f97316, #ea580c);
border: none;
border-radius: 12px;
color: white;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
}
.oll-send-btn:hover { opacity: 0.9; }
.oll-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.oll-chat-offline {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
color: #64748b;
padding: 40px;
}
.oll-offline-icon { font-size: 48px; margin-bottom: 16px; }
.oll-chat-offline h3 { margin: 0 0 8px; color: #f8fafc; }
.oll-chat-offline p { margin: 0 0 16px; }
.oll-chat-offline code {
background: #1e293b;
padding: 8px 16px;
border-radius: 8px;
font-size: 13px;
}
`;
}
});