mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-01 16:29:29 +00:00
Merge pull request #4485 from Sub2API-Devs/dev
feat(security-audit): 新增 OpenAI 兼容提示词审计能力与安全审计控制台
This commit is contained in:
@@ -5,6 +5,7 @@ import resources from './resources'
|
||||
import ops from './ops'
|
||||
import settings from './settings'
|
||||
import audit from './audit'
|
||||
import promptAudit from './promptAudit'
|
||||
|
||||
export default {
|
||||
...overview,
|
||||
@@ -14,4 +15,5 @@ export default {
|
||||
...ops,
|
||||
...settings,
|
||||
...audit,
|
||||
...promptAudit,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
export default {
|
||||
promptAudit: {
|
||||
title: 'Prompt Audit',
|
||||
description: 'Review user input asynchronously or block it synchronously through OpenAI-compatible Qwen3Guard nodes. Full prompts are stored with events for admin review.',
|
||||
configVersion: 'Config version v{version}',
|
||||
tabs: { config: 'Configuration', events: 'Events' },
|
||||
actions: { refresh: 'Refresh runtime', retry: 'Retry', Allow: 'Allow', Warn: 'Warn', Block: 'Block' },
|
||||
common: { actions: 'Actions', never: 'Never' },
|
||||
mode: { off: 'Off', async_audit: 'Async audit only', blocking: 'Synchronous audit and block' },
|
||||
status: { disabled: 'Disabled', running: 'Running', degraded: 'Degraded', error: 'Error', healthy: 'Healthy', failed: 'Failed', stale: 'Stale heartbeat' },
|
||||
decisions: { pass: 'Pass', flag: 'Flag', critical: 'Critical' },
|
||||
riskLevels: { low: 'Low', medium: 'Medium', high: 'High', critical: 'Critical' },
|
||||
scanners: {
|
||||
violent: 'Violent',
|
||||
non_violent_illegal_acts: 'Non-violent Illegal Acts',
|
||||
sexual_content_or_sexual_acts: 'Sexual Content or Sexual Acts',
|
||||
pii: 'PII',
|
||||
suicide_and_self_harm: 'Suicide & Self-Harm',
|
||||
unethical_acts: 'Unethical Acts',
|
||||
politically_sensitive_topics: 'Politically Sensitive Topics',
|
||||
copyright_violation: 'Copyright Violation',
|
||||
jailbreak: 'Jailbreak',
|
||||
},
|
||||
scannerDescriptions: {
|
||||
violent: 'Violence or threats of violence',
|
||||
non_violent_illegal_acts: 'Non-violent illegal activity',
|
||||
sexual_content_or_sexual_acts: 'Sexual content or sexual acts',
|
||||
pii: 'Personal identifying information',
|
||||
suicide_and_self_harm: 'Suicide or self-harm',
|
||||
unethical_acts: 'Unethical behavior',
|
||||
politically_sensitive_topics: 'Politically sensitive topics',
|
||||
copyright_violation: 'Copyright infringement',
|
||||
jailbreak: 'Prompt injection or jailbreak attempt',
|
||||
},
|
||||
runtime: {
|
||||
title: 'Runtime overview',
|
||||
description: 'Shows the configuration currently active on the server. Unsaved draft changes do not affect these values.',
|
||||
process: 'Process status', mode: 'Effective mode', version: 'Active / expected version', workers: 'Active / total workers',
|
||||
queue: 'Active jobs / capacity', dependencies: 'Dependencies', guardMetrics: 'Synchronous Guard metrics', latest: 'Latest processing and error',
|
||||
queueBreakdown: 'queued {queued} · processing {processing} · retry {retry} · done {done} · failed {failed}',
|
||||
deliveryTotals: 'Total enqueued {enqueued} · dropped {dropped} · processed {processed} · failed {failed}',
|
||||
},
|
||||
metrics: { total: 'Total', allowed: 'Allowed', flagged: 'Flagged', blocked: 'Blocked', unavailable: 'Unavailable', timeouts: 'Timeouts', failovers: 'Failovers' },
|
||||
pool: {
|
||||
title: 'Audit pool', description: 'Enabled OpenAI-compatible nodes are tried in order. Probes run from the server network.',
|
||||
add: 'Add node', edit: 'Edit node', empty: 'No audit nodes configured.', node: 'Node', model: 'Model', limits: 'Timeout / chunk limit', credential: 'Credential and probe',
|
||||
configured: 'API Key configured', missing: 'API Key missing', probe: 'Test connection', probing: 'Probing…',
|
||||
probeProgress: 'Config validated ✓ · request sent · awaiting service response…', probeResult: 'Config ✓ · request ✓ · HTTP {http} · {status} · {latency} ms',
|
||||
name: 'Node name', id: 'Stable node ID', baseUrl: 'Base URL', apiKey: 'API Key', keepSecret: 'Leave blank to keep the saved API Key',
|
||||
secretHint: 'Plaintext exists only in this editor and is cleared immediately after a successful save.', clearSecret: 'Explicitly clear the saved API Key', timeout: 'Total timeout (ms)', inputLimit: 'Unicode characters per chunk',
|
||||
toggleNode: 'Toggle node {name}', deleteConfirm: 'Remove “{name}” from the draft? It takes effect after saving.',
|
||||
},
|
||||
policy: {
|
||||
title: 'Audit policy', description: 'Configure group scope, nine input-risk categories, workers, and queue bounds.', scope: 'Scope', allGroups: 'All groups', selectedGroups: 'Selected groups',
|
||||
searchGroups: 'Search groups', noGroups: 'No matching groups', missingGroups: 'Configured IDs for groups that no longer exist', selectedCount: '{count} groups selected',
|
||||
scanners: 'Qwen3Guard input-risk categories', workerCount: 'Worker count', queueCapacity: 'Persistent queue capacity', strategy: 'Node strategy', strategyHint: 'Try nodes in configuration order and fail over when allowed.',
|
||||
},
|
||||
saveBar: { enabled: 'Enable prompt audit', blocking: 'Synchronous blocking', storePass: 'Store safe events', dirty: 'Unsaved changes', synced: 'Configuration synced' },
|
||||
blockingConfirm: {
|
||||
title: 'Enable synchronous blocking?',
|
||||
message: 'Applicable requests wait for Guard before account selection, billing, or upstream access. Block, unavailable Guard, and invalid responses all prevent upstream access.',
|
||||
confirm: 'I understand; enable it',
|
||||
},
|
||||
events: {
|
||||
title: 'Audit events', description: 'Review events by identity, route, risk, hash, and time; the detail view shows the full prompt.', decision: 'Decision', risk: 'Risk level', endpoint: 'Endpoint', groupId: 'Group ID', userId: 'User ID', apiKeyId: 'API Key ID', keyword: 'Keyword',
|
||||
startAt: 'Start time', endAt: 'End time', deleteSelected: 'Delete selected ({count})', deleteByFilter: 'Delete by filter',
|
||||
filterDeleteDialogTitle: 'Delete audit events by filter', filterDeleteDialogDesc: 'Choose the time range and risk criteria, then delete directly. Deletion is permanent. Generate a preview first if you want to see the match count.',
|
||||
filterTimeRange: 'Deletion time range', filterTimeRangeHint: 'Deletes events created before the selected cutoff. Events created after the preview are not affected.',
|
||||
timePresets: { '1d': 'Older than 1 day', '7d': 'Older than 7 days', '30d': 'Older than 30 days', '90d': 'Older than 90 days', all: 'All time', custom: 'Custom range' },
|
||||
customRangeInvalid: 'A custom range needs a valid start and end time, with the start before the end.',
|
||||
moreConditions: 'More conditions (endpoint / keyword / group / user)',
|
||||
filterDeletePreviewAction: 'Generate delete preview', filterDeletePreviewing: 'Generating preview…', filterDeleteNeedPreview: 'You can delete directly, or generate a preview first to see the match count.',
|
||||
filterDeleteConfirmInvalidRange: 'Select a valid deletion time range first (a custom range needs a start before the end).', filterDeleteConfirmNoMatches: 'The current filters matched 0 events, so there is nothing to delete.',
|
||||
selectAll: 'Select all events on this page', selectEvent: 'Select event {id}', time: 'Time', identity: 'User / email / API Key', user: 'Username', email: 'User email', apiKey: 'API Key name', group: 'Group', route: 'Endpoint / model', result: 'Decision / risk', preview: 'Redacted preview', empty: 'No matching events.',
|
||||
passEventsDisabled: '“Store safe events” is off. Safe requests are still audited but do not appear in this list; Flag and Critical risk events are still stored.', openConfiguration: 'Open configuration',
|
||||
detailTitle: 'Prompt audit event details', tabs: { summary: 'Audit summary', risks: 'Specific risks', technical: 'Technical details' },
|
||||
promptFull: 'Full prompt (unredacted)',
|
||||
promptFullHint: 'The full prompt is stored with this event for admin review only. Treat it as sensitive data and do not share it.',
|
||||
guardReturn: 'Model audit return',
|
||||
guardReturnHint: 'Normalized Guard result (decision, categories, scores, and redacted evidence). Raw response bodies are not stored.',
|
||||
riskSummaries: 'Risk summaries',
|
||||
evidence: 'Redacted evidence',
|
||||
score: 'Score',
|
||||
categories: 'Categories', model: 'Model', stage: 'Request stage', noRisks: 'No derived risk summaries for this event.',
|
||||
requestId: 'Request ID', promptHash: 'Prompt SHA-256',
|
||||
technical: {
|
||||
scanner: 'Scanner', policy: 'Policy', guardEndpoint: 'Guard endpoint', config: 'Config',
|
||||
chunks: 'Chunks', latency: 'Latency', protocol: 'Protocol',
|
||||
},
|
||||
deleteConfirmTitle: 'Delete audit events?', deleteConfirmMessage: 'This permanently deletes {count} events and eligible orphan jobs.', filterDeleteCount: 'The server snapshot matches {count} events.', snapshotMax: 'Snapshot maximum event ID', expiresAt: 'Confirmation token expires', filterDeleteWarning: 'Only events at or below the preview high-water mark are deleted. Newer events survive. Any filter change requires a new preview.', confirmFilterDelete: 'Permanently delete',
|
||||
},
|
||||
messages: { saved: 'Prompt Audit configuration saved; plaintext API Key state was cleared.', probeSucceeded: 'The audit node is reachable.', deleted: 'Deleted {count} audit events.' },
|
||||
errors: {
|
||||
loadConfig: 'Unable to load Prompt Audit configuration.', loadRuntime: 'Unable to load Prompt Audit runtime.', loadGroups: 'Unable to load groups.', loadEvents: 'Unable to load audit events.', loadDetail: 'Unable to load event details.', saveConfig: 'Unable to save the configuration.', probe: 'Node probe failed.', delete: 'Unable to delete events.', previewDelete: 'Unable to create a deletion preview. Check the time range.', deleteConfirmation: 'The deletion confirmation is invalid or expired. Preview again.',
|
||||
prompt_audit_config_conflict: 'Another administrator updated this configuration. Reload the server version before deciding how to merge your draft.',
|
||||
prompt_guard_requires_audit_enabled: 'Enable Prompt Audit before synchronous blocking.', prompt_audit_invalid_endpoint: 'The audit node configuration is invalid.', prompt_audit_endpoint_required: 'Enable at least one audit node before enabling Prompt Audit.', prompt_audit_groups_required: 'Select at least one group in selected-group mode.', prompt_audit_scanners_required: 'Enable at least one risk category.',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -190,6 +190,9 @@ export default {
|
||||
channelMonitor: 'Channel Monitor',
|
||||
channelStatus: 'Channel Status',
|
||||
riskControl: 'Risk Control',
|
||||
securityAudit: 'Security Audit',
|
||||
contentModeration: 'Content Moderation',
|
||||
promptAudit: 'Prompt Audit',
|
||||
auditLogs: 'Audit Logs',
|
||||
},
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import resources from './resources'
|
||||
import ops from './ops'
|
||||
import settings from './settings'
|
||||
import audit from './audit'
|
||||
import promptAudit from './promptAudit'
|
||||
|
||||
export default {
|
||||
...overview,
|
||||
@@ -14,4 +15,5 @@ export default {
|
||||
...ops,
|
||||
...settings,
|
||||
...audit,
|
||||
...promptAudit,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
export default {
|
||||
promptAudit: {
|
||||
title: '提示词审计',
|
||||
description: '通过 OpenAI 兼容 Qwen3Guard 节点异步复核或同步阻止用户输入;事件的完整提示词会入库保存,仅供管理员复核。',
|
||||
configVersion: '配置版本 v{version}',
|
||||
tabs: { config: '配置', events: '事件' },
|
||||
actions: { refresh: '刷新运行态', retry: '重试', Allow: '放行', Warn: '警告', Block: '阻止' },
|
||||
common: { actions: '操作', never: '从未' },
|
||||
mode: { off: '已关闭', async_audit: '异步只审计', blocking: '同步审计并阻止' },
|
||||
status: { disabled: '未启用', running: '运行中', degraded: '降级', error: '错误', healthy: '健康', failed: '失败', stale: '心跳过期' },
|
||||
decisions: { pass: '通过', flag: '标记', critical: '严重' },
|
||||
riskLevels: { low: '低', medium: '中', high: '高', critical: '严重' },
|
||||
scanners: {
|
||||
violent: '暴力',
|
||||
non_violent_illegal_acts: '非暴力违法行为',
|
||||
sexual_content_or_sexual_acts: '色情内容或性行为',
|
||||
pii: '个人身份信息',
|
||||
suicide_and_self_harm: '自杀与自残',
|
||||
unethical_acts: '不道德行为',
|
||||
politically_sensitive_topics: '政治敏感话题',
|
||||
copyright_violation: '版权侵犯',
|
||||
jailbreak: '越狱',
|
||||
},
|
||||
scannerDescriptions: {
|
||||
violent: '暴力或暴力威胁',
|
||||
non_violent_illegal_acts: '非暴力违法活动',
|
||||
sexual_content_or_sexual_acts: '色情内容或性行为',
|
||||
pii: '个人身份信息',
|
||||
suicide_and_self_harm: '自杀或自残',
|
||||
unethical_acts: '不道德行为',
|
||||
politically_sensitive_topics: '政治敏感话题',
|
||||
copyright_violation: '版权侵权',
|
||||
jailbreak: '提示注入或越狱尝试',
|
||||
},
|
||||
runtime: {
|
||||
title: '运行概览',
|
||||
description: '显示服务端当前生效状态;未保存的草稿不会改变这些数值。',
|
||||
process: '进程状态', mode: '生效模式', version: '生效 / 期望版本', workers: '活动 / 总 Worker',
|
||||
queue: '活动任务 / 容量', dependencies: '依赖', guardMetrics: '同步 Guard 指标', latest: '最近处理与错误',
|
||||
queueBreakdown: 'queued {queued} · processing {processing} · retry {retry} · done {done} · failed {failed}',
|
||||
deliveryTotals: '累计入队 {enqueued} · 丢弃 {dropped} · 处理 {processed} · 失败 {failed}',
|
||||
},
|
||||
metrics: { total: '总计', allowed: '放行', flagged: '标记', blocked: '阻止', unavailable: '不可用', timeouts: '超时', failovers: '故障切换' },
|
||||
pool: {
|
||||
title: '审计池', description: '按顺序使用启用的 OpenAI 兼容节点;探测由服务端真实网络环境发起。',
|
||||
add: '新增节点', edit: '编辑节点', empty: '尚未配置审计节点。', node: '节点', model: '模型', limits: '超时 / 单片上限', credential: '凭据与探测',
|
||||
configured: 'API Key 已配置', missing: '未配置 API Key', probe: '连接测试', probing: '探测中…',
|
||||
probeProgress: '配置校验 ✓ · 请求已发送 · 等待服务响应…', probeResult: '配置校验 ✓ · 请求 ✓ · HTTP {http} · {status} · {latency} ms',
|
||||
name: '节点名称', id: '稳定节点 ID', baseUrl: 'Base URL', apiKey: 'API Key', keepSecret: '留空以保留已保存的 API Key',
|
||||
secretHint: '明文只在本次编辑内存中存在;保存成功后会立即清除。', clearSecret: '显式清除已保存的 API Key', timeout: '总超时(毫秒)', inputLimit: '单片 Unicode 字符上限',
|
||||
toggleNode: '切换节点 {name}', deleteConfirm: '从草稿中删除节点“{name}”?保存配置后生效。',
|
||||
},
|
||||
policy: {
|
||||
title: '审计策略', description: '配置适用分组、九类输入风险、Worker 与队列边界。', scope: '适用范围', allGroups: '全部分组', selectedGroups: '指定分组',
|
||||
searchGroups: '搜索分组', noGroups: '没有匹配分组', missingGroups: '配置中包含已删除的分组 ID', selectedCount: '已选择 {count} 个分组',
|
||||
scanners: 'Qwen3Guard 输入风险分类', workerCount: 'Worker 数量', queueCapacity: '持久队列容量', strategy: '节点策略', strategyHint: '按配置顺序优先尝试,必要时故障切换。',
|
||||
},
|
||||
saveBar: { enabled: '启用提示词审计', blocking: '同步阻止', storePass: '保存安全事件', dirty: '有未保存的更改', synced: '配置已同步' },
|
||||
blockingConfirm: {
|
||||
title: '开启同步阻止?',
|
||||
message: '适用请求会在账号选择、计费和访问上游之前等待 Guard。命中 Block、Guard 不可用或响应非法时,请求都不会访问上游。',
|
||||
confirm: '理解风险并开启',
|
||||
},
|
||||
events: {
|
||||
title: '审计事件', description: '按身份、入口、风险、Hash 和时间复核事件,详情中可查看完整提示词。', decision: '判定', risk: '风险等级', endpoint: '入口', groupId: '分组 ID', userId: '用户 ID', apiKeyId: 'API Key ID', keyword: '关键词',
|
||||
startAt: '开始时间', endAt: '结束时间', deleteSelected: '删除选中项({count})', deleteByFilter: '按筛选删除',
|
||||
filterDeleteDialogTitle: '按筛选删除审计事件', filterDeleteDialogDesc: '选择删除的时间范围与风险条件后即可执行删除;删除不可恢复。如需提前查看匹配数量,可先获取删除预览。',
|
||||
filterTimeRange: '删除时间范围', filterTimeRangeHint: '将删除所选截止时间之前产生的事件;预览后新产生的事件不受影响。',
|
||||
timePresets: { '1d': '1 天前', '7d': '7 天前', '30d': '30 天前', '90d': '90 天前', all: '全部时间', custom: '自定义范围' },
|
||||
customRangeInvalid: '自定义范围需要有效的开始与结束时间,且开始早于结束。',
|
||||
moreConditions: '更多条件(入口 / 关键词 / 分组 / 用户)',
|
||||
filterDeletePreviewAction: '获取删除预览', filterDeletePreviewing: '正在生成预览…', filterDeleteNeedPreview: '可直接确认删除;如需提前查看匹配数量,可先获取删除预览。',
|
||||
filterDeleteConfirmInvalidRange: '请先选择有效的删除时间范围(自定义范围需开始早于结束)。', filterDeleteConfirmNoMatches: '当前筛选匹配 0 条事件,没有可删除的内容。',
|
||||
selectAll: '选择当前页全部事件', selectEvent: '选择事件 {id}', time: '时间', identity: '用户 / 邮箱 / API Key', user: '用户名', email: '用户邮箱', apiKey: 'API Key 名称', group: '分组', route: '入口 / 模型', result: '判定 / 风险', preview: '脱敏预览', empty: '没有符合条件的事件。',
|
||||
passEventsDisabled: '当前未开启“保存安全事件”:安全请求仍会完成审计,但不会出现在事件列表中;Flag 和 Critical 风险事件仍会保存。', openConfiguration: '前往配置',
|
||||
detailTitle: '提示词审计事件详情', tabs: { summary: '审计摘要', risks: '具体风险', technical: '技术信息' },
|
||||
promptFull: '完整提示词(未脱敏)',
|
||||
promptFullHint: '完整提示词已随事件入库,仅供管理员复核触发内容;请按敏感数据妥善处理,切勿外泄。',
|
||||
guardReturn: '模型审计返回',
|
||||
guardReturnHint: '展示 Guard 归一化后的结构化结果(判定、分类、分数与脱敏证据),不含原始响应体。',
|
||||
riskSummaries: '风险摘要',
|
||||
evidence: '脱敏证据',
|
||||
score: '分数',
|
||||
categories: '分类', model: '模型', stage: '请求阶段', noRisks: '本事件没有派生风险摘要。',
|
||||
requestId: 'Request ID', promptHash: 'Prompt SHA-256',
|
||||
technical: {
|
||||
scanner: '扫描器', policy: '策略', guardEndpoint: 'Guard 节点', config: '配置版本',
|
||||
chunks: '分片数', latency: '耗时', protocol: '协议',
|
||||
},
|
||||
deleteConfirmTitle: '删除审计事件?', deleteConfirmMessage: '将永久删除 {count} 条事件及符合条件的孤立任务。', filterDeleteCount: '服务端快照匹配 {count} 条事件。', snapshotMax: '快照最大事件 ID', expiresAt: '确认令牌过期时间', filterDeleteWarning: '只删除预览高水位内的事件;预览后产生的新事件会保留。筛选一旦变化,必须重新预览。', confirmFilterDelete: '确认永久删除',
|
||||
},
|
||||
messages: { saved: '提示词审计配置已保存,明文 API Key 状态已清除。', probeSucceeded: '审计节点连接正常。', deleted: '已删除 {count} 条审计事件。' },
|
||||
errors: {
|
||||
loadConfig: '无法加载提示词审计配置。', loadRuntime: '无法加载提示词审计运行态。', loadGroups: '无法加载分组列表。', loadEvents: '无法加载审计事件。', loadDetail: '无法加载事件详情。', saveConfig: '配置保存失败。', probe: '节点探测失败。', delete: '事件删除失败。', previewDelete: '无法生成删除预览,请检查时间范围。', deleteConfirmation: '删除确认无效或已过期,请重新预览。',
|
||||
prompt_audit_config_conflict: '配置已被其他管理员更新。请重新加载服务端配置,再决定如何合并本地草稿。',
|
||||
prompt_guard_requires_audit_enabled: '开启同步阻止前必须先启用提示词审计。', prompt_audit_invalid_endpoint: '审计节点配置无效。', prompt_audit_endpoint_required: '启用审计前至少需要一个启用节点。', prompt_audit_groups_required: '指定分组模式至少需要选择一个分组。', prompt_audit_scanners_required: '至少需要启用一个风险分类。',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -190,6 +190,9 @@ export default {
|
||||
channelMonitor: '渠道监控',
|
||||
channelStatus: '渠道状态',
|
||||
riskControl: '风控中心',
|
||||
securityAudit: '安全审计',
|
||||
contentModeration: '内容审核',
|
||||
promptAudit: '提示词审计',
|
||||
auditLogs: '操作日志',
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user