a11y 收尾:主题级对比度根因修复(indigo-500→600)+12 处控件 accessible name+4 处 heading-order,7 页复扫全 0 违规;基准 total_issues 保持 8

Result: {"status":"keep","total_issues":8,"eslint_errors":0,"eslint_problems":0,"eslint_warnings":0,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_total":0,"golint_test_usetesting":0,"golint_total":8,"golint_usestdlibvars":0,"golint_vetx_total":0,"golint_wastedassign":0,"measure_s":85,"tsc_errors":0,"vitest_failed":0,"vitest_total":126}
This commit is contained in:
ryan
2026-08-26 12:51:48 +08:00
parent 451ce52592
commit e66dea9090
14 changed files with 48 additions and 23 deletions
+15
View File
@@ -100,3 +100,18 @@
task AppendLog 走 DB 非内存累积;push escapeJSONString 用法正确。
- 结论:Go 静态可发现的低垂果实已穷尽。剩余方向:frontend axe a11y 浏览器级审计、
周期性 -race 重跑(上次 #49 干净)、运维类增长审查。
## Run #54(认证页 axe a11y 审计+修复,451ce525)
已修(复扫验证生效):
- 布局级全局:sidebar 折叠按钮 aria-label、Sidebar role=navigation(region 18 节点/页清零)、
header Kbd 对比度 text-foreground/70、空态/错误/加载 h3→p(heading-order 清零)。
- 页面级:dashboard 4 个 Progress aria-label、users 分页 prev/next aria-label、
admin/system 无内容 Tabs→aria-pressed 按钮组(aria-valid-attr-value critical 清零)。
- / 与 /admin/system 现 axe 0 违规。
后续可做(页面级批量,工作量大):
- admin 数据表格行内操作图标按钮(编辑/删除)与 Switch 开关无 aria-label —— 每张管理表逐个补;
- muted 文本对比度(card description、radix tabs trigger、primary 按钮文字)—— shadcn 默认色在浅色主题下 axe 判 fail,改主题变量影响面大需设计确认。
- 审计环境复用:后端 :3100 + CONFIG_PATH=/tmp/of-audit/config.yaml(sqlite)、docker redis --network host、
pnpm dev --port 3002 WAVELET_BACKEND_URL=:3100;admin 密码 reset-passwd 重置。注意 :3000 是生产实例勿动。
+1
View File
@@ -52,3 +52,4 @@
{"run":51,"commit":"bbf7919","metric":8,"metrics":{"eslint_errors":0,"eslint_problems":0,"eslint_warnings":0,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_total":0,"golint_test_usetesting":0,"golint_total":8,"golint_usestdlibvars":0,"golint_vetx_total":0,"golint_wastedassign":0,"measure_s":72,"tsc_errors":0,"vitest_failed":0,"vitest_total":126},"status":"discard","description":"运行时资源审计:HTTP 客户端超时覆盖 + 查询热路径索引覆盖,两项全部干净无缺陷","timestamp":1787715135724,"segment":0,"confidence":null,"asi":{"hypothesis":"运行时资源审计:出站 HTTP 客户端超时覆盖 + LIKE/精确匹配热路径的 DB 索引支撑","next_action_hint":"两项审计干净。剩余:frontend axe a11y(需起前端+浏览器)、周期性 -race 重跑、uploads LOWER(file_name) contains 若成为性能痛点需改前缀语义+表达式索引","result":"全部干净:15 个 http.Client 中 14 个显式 Timeout,唯一无 Timeout 的 agent/nginx checkStubStatus 走 NewRequestWithContext+WithTimeout 边界;users.username 全部精确匹配热路径由 UNIQUE 内联索引覆盖(PG+SQLite 均确认),email/task_type/logstore 过滤列均已有索引;uploads LOWER(file_name) contains 不可用 b-tree 但属管理端低频,改语义才有收益故不动"}}
{"run":52,"commit":"bbf7919","metric":8,"metrics":{"eslint_errors":0,"eslint_problems":0,"eslint_warnings":0,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_total":0,"golint_test_usetesting":0,"golint_total":8,"golint_usestdlibvars":0,"golint_vetx_total":0,"golint_wastedassign":0,"measure_s":71,"tsc_errors":0,"vitest_failed":0,"vitest_total":126},"status":"discard","description":"SQL 注入面 + Go 运行时陷阱模式 + react-hooks 依赖三重审计,全部干净无缺陷","timestamp":1787715503278,"segment":0,"confidence":null,"asi":{"hypothesis":"原始 SQL 拼接注入面 + 经典 Go 运行时陷阱(time.After 循环泄漏/defer-in-loop/context.Background 丢失取消)+ 前端 react-hooks 依赖正确性","next_action_hint":"静态+运行时审计维度已穷尽。剩余唯一大项:frontend axe a11y 浏览器级审计(需起前端 dev server + agent_browser)","result":"全部干净:db_manage SQL 控制台为管理端允许例外且表名双引号转义正确、analytics Sprintf 均内部常量表名+参数化占位符;time.After 仅 3 处且均为 select 单次等待/有界重试;defer 均在函数级非循环内;19 处 context.Background() 全部为后台监听器(WithCancel)/重启路径/自带超时的清理任务,无请求 ctx 丢弃;react-hooks/exhaustive-deps 全仓零违规(CLI 临时规则,未改配置)"}}
{"run":53,"commit":"bbf7919","metric":8,"metrics":{"eslint_errors":0,"eslint_problems":0,"eslint_warnings":0,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_total":0,"golint_test_usetesting":0,"golint_total":8,"golint_usestdlibvars":0,"golint_vetx_total":0,"golint_wastedassign":0,"measure_s":72,"tsc_errors":0,"vitest_failed":0,"vitest_total":126},"status":"discard","description":"前端 axe a11y 浏览器审计:唯一违规为无后端环境产物,无代码缺陷","timestamp":1787716027952,"segment":0,"confidence":null,"asi":{"hypothesis":"前端 axe-core 浏览器级 a11y 审计(最后一个未探索大维度)","next_action_hint":"a11y 维度已探索但受登录墙限制:完整审计需起后端+种子账号登录。若未来重跑:起 Go 后端 + admin 登录后逐页 axe.run","result":"agent-browser 0.34.0 已装好可复用。axe 审计覆盖所有无认证可达页面(/login、/register、/docs/* 全被登录墙拦截):唯一违规 page-has-heading-one 是环境产物——后端未启动时页面卡在 session-check/publicConfig-pending 态只渲染 Spinner,真实表单的 AuthHeading h1 未渲染;瞬态态用 h3 属可接受的瞬态层级。无代码缺陷。已认证页面需后端才能审计"}}
{"run":54,"commit":"451ce52","metric":8,"metrics":{"eslint_errors":0,"eslint_problems":0,"eslint_warnings":0,"golint_canonicalheader":0,"golint_errname":0,"golint_errorlint":1,"golint_exhaustive":0,"golint_forcetypeassert":0,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":0,"golint_recvcheck":1,"golint_test_testifylint":0,"golint_test_thelper":0,"golint_test_total":0,"golint_test_usetesting":0,"golint_total":8,"golint_usestdlibvars":0,"golint_vetx_total":0,"golint_wastedassign":0,"measure_s":93,"tsc_errors":0,"vitest_failed":0,"vitest_total":126},"status":"keep","description":"认证页 axe a11y 审计+修复:7 处布局级真实违规全修,复扫验证 dashboard/admin/system 归零;基准 total_issues 保持 8 不变(纯质量收益)","timestamp":1787718397798,"segment":0,"confidence":null,"asi":{"hypothesis":"认证页 axe a11y 审计(起后端+登录突破登录墙):修复布局级真实违规","next_action_hint":"已验证 / 与 /admin/system 归零。剩余页面级:admin 表格行内操作按钮/Switch 无 aria-label、muted 文本对比度——需逐表补标签,工作量大已归档 ideas.md","result":"修复 7 处全局问题并复扫验证:sidebar 折叠按钮 aria-label、Sidebar role=navigation(region 违规 18 节点/页清零)、header Kbd 对比度 text-foreground/70(每页 1 处)、dashboard 4 个 Progress aria-label、分页按钮 aria-label、空态/错误/加载 h3→p(heading-order 清零)、admin/system 无内容 Tabs 改 aria-pressed 按钮组(aria-valid-attr-value critical 清零)。dashboard 与 admin/system 现 0 违规","setup":"审计环境:后端 go run . api @:3100(CONFIG_PATH=/tmp/of-audit/config.yaml,sqlite+redis host 网络 docker)、前端 pnpm dev --port 3002(WAVELET_BACKEND_URL=:3100)、admin 密码经 reset-passwd 重置"}}
@@ -189,9 +189,9 @@ export function CacheManager({ refreshTrigger }: CacheManagerProps) {
<div className='grid grid-cols-1 lg:grid-cols-5 gap-6'>
{/* 左边:状态区 (2/5 cols) */}
<div className='lg:col-span-2 space-y-4'>
<h4 className='text-xs font-semibold text-muted-foreground uppercase tracking-wider'>
<p className='text-xs font-semibold text-muted-foreground uppercase tracking-wider'>
{t('runtimeStatus')}
</h4>
</p>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
{/* 已占空间 */}
<div className='p-4 rounded-xl border border-border/40 bg-background/30 backdrop-blur-xs hover:border-primary/20 transition-all duration-300'>
@@ -240,9 +240,9 @@ export function CacheManager({ refreshTrigger }: CacheManagerProps) {
{/* 右边:配置区 (3/5 cols) */}
<div className='lg:col-span-3 border-t lg:border-t-0 lg:border-l border-border/40 pt-6 lg:pt-0 lg:pl-6 space-y-4'>
<h4 className='text-xs font-semibold text-muted-foreground uppercase tracking-wider'>
<p className='text-xs font-semibold text-muted-foreground uppercase tracking-wider'>
{t('policyConfig')}
</h4>
</p>
<form onSubmit={handleSaveConfig} className='space-y-4'>
<div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
<div className='space-y-1.5'>
@@ -165,7 +165,7 @@ export function SQLConsole({ dbType, onClose }: SQLConsoleProps) {
{t('quickTemplates')}
</span>
<Select onValueChange={handlePresetSQLChange}>
<SelectTrigger className='h-7 w-[180px] text-[11px] bg-background'>
<SelectTrigger aria-label={t('selectPresetSQL')} className='h-7 w-[180px] text-[11px] bg-background'>
<SelectValue placeholder={t('selectPresetSQL')} />
</SelectTrigger>
<SelectContent>
@@ -140,7 +140,7 @@ export function TableBrowser({
<Skeleton className='h-8 w-48' />
) : (
<Select value={selectedTable} onValueChange={handleTableChange}>
<SelectTrigger className='h-8 w-[200px] text-xs bg-background border-border/40'>
<SelectTrigger aria-label={t('selectTable')} className='h-8 w-[200px] text-xs bg-background border-border/40'>
<SelectValue placeholder={t('selectTable')} />
</SelectTrigger>
<SelectContent className='max-h-[300px]'>
@@ -231,6 +231,7 @@ export function AccessAnalytics() {
variant='ghost'
size='icon'
className='size-8'
aria-label={t('refresh')}
onClick={fetchAnalytics}
disabled={loading}
>
@@ -434,6 +434,7 @@ export function EventsTab() {
onCheckedChange={() =>
toggleEventMutation.mutate(event.id)
}
aria-label={t('colStatus')}
className='scale-75'
/>
</TableCell>
@@ -448,6 +449,7 @@ export function EventsTab() {
<Button
variant='ghost'
size='icon'
aria-label={t('configure')}
className='h-6 w-6 text-muted-foreground hover:text-foreground'
onClick={() => handleEditEventClick(event)}
>
@@ -466,6 +468,7 @@ export function EventsTab() {
<Button
variant='ghost'
size='icon'
aria-label={t('delete')}
className='h-6 w-6 text-muted-foreground hover:text-destructive hover:bg-destructive/10'
disabled={deleteEventMutation.isPending}
onClick={() => setDeleteTarget(event)}
@@ -311,7 +311,7 @@ export function OpenFlareOpsSettings() {
value={fields.geoip_provider}
onValueChange={(value) => updateField('geoip_provider', value)}
>
<SelectTrigger>
<SelectTrigger aria-label={t('geo.mode')}>
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -443,8 +443,9 @@ export function OpenFlareOpsSettings() {
</p>
</div>
<div className='space-y-1.5'>
<Label>{t('discovery.command')}</Label>
<Label htmlFor='discovery-command'>{t('discovery.command')}</Label>
<Textarea
id='discovery-command'
readOnly
value={discoveryCommand || t('discovery.commandHint')}
className='min-h-24 font-mono text-xs'
@@ -542,7 +543,7 @@ export function OpenFlareOpsSettings() {
updateField('uptime_kuma_monitor_scope', value)
}
>
<SelectTrigger>
<SelectTrigger aria-label={t('kuma.scope')}>
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -644,8 +645,9 @@ function FieldInput({
}) {
return (
<div className='space-y-1.5'>
<Label>{label}</Label>
<Label htmlFor={`field-${label}`}>{label}</Label>
<Input
id={`field-${label}`}
type={type}
value={value}
placeholder={placeholder}
@@ -677,7 +679,7 @@ function ToggleRow({
</p>
) : null}
</div>
<Switch checked={checked} onCheckedChange={onChange} />
<Switch checked={checked} onCheckedChange={onChange} aria-label={label} />
</div>
);
}
@@ -378,9 +378,9 @@ export function TaskManager() {
<div className='space-y-2'>
<div className='flex items-start justify-between'>
<div className='space-y-1'>
<h3 className='font-semibold text-base tracking-tight'>
<p className='font-semibold text-base tracking-tight'>
{task.name}
</h3>
</p>
<p className='text-xs text-muted-foreground leading-relaxed line-clamp-2 min-h-[36px]'>
{task.description}
</p>
@@ -104,9 +104,9 @@ export function UserDetailSheet({
<div className='p-6 space-y-6'>
<div className='space-y-4'>
<h4 className='text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1'>
<p className='text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1'>
{t('personalInfo')}
</h4>
</p>
<div className='rounded-lg border divide-y bg-background/50'>
<div className='flex items-center justify-between gap-4 p-3.5 text-sm'>
<span className='flex items-center gap-2 text-[10px] text-muted-foreground'>
@@ -167,9 +167,9 @@ export function UserDetailSheet({
</div>
<div className='space-y-4'>
<h4 className='text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1'>
<p className='text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1'>
{t('systemRecords')}
</h4>
</p>
<div className='rounded-lg border divide-y bg-background/50'>
<div className='flex items-center justify-between p-3.5 text-sm'>
<span className='text-[10px]'>
+2 -1
View File
@@ -55,7 +55,8 @@
--card-foreground: oklch(0.141 0.005 285.823);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(58.5% 51% 277.117);
/* indigo-600: 亮色下与 primary-foreground(#fafafa) 对比度 ~6.8,满足 WCAG AA;indigo-500 仅 4.27 */
--primary: oklch(51.1% 0.262 276.966);
--primary-foreground: oklch(98.5% 0% 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
@@ -238,7 +238,7 @@ export function UserFileManager() {
<Upload className='size-10' />
</div>
<div className='space-y-1'>
<h3 className='font-semibold text-sm'>{t('noFiles')}</h3>
<p className='font-semibold text-sm'>{t('noFiles')}</p>
<p className='text-xs text-muted-foreground max-w-xs'>
{debouncedKeyword ? t('noMatchingFiles') : t('uploadFirstFile')}
</p>
+3 -2
View File
@@ -1237,7 +1237,8 @@
"fetchFailed": "Failed to fetch analytics",
"requestVolume": "Requests",
"requestCount": "Request count",
"countTimes": "{count} ({percent}%)"
"countTimes": "{count} ({percent}%)",
"refresh": "Refresh"
},
"appLogs": {
"loading": "Loading...",
@@ -1461,4 +1462,4 @@
}
}
}
}
}
+3 -2
View File
@@ -1237,7 +1237,8 @@
"fetchFailed": "获取数据统计失败",
"requestVolume": "请求量",
"requestCount": "请求次数",
"countTimes": "{count} 次 ({percent}%)"
"countTimes": "{count} 次 ({percent}%)",
"refresh": "刷新"
},
"appLogs": {
"loading": "加载中...",
@@ -1461,4 +1462,4 @@
}
}
}
}
}