forked from wangziqi/gongxue-base
94 lines
4.2 KiB
SQL
94 lines
4.2 KiB
SQL
alter table public.content_assets
|
|
add column if not exists security_scan_status text not null default 'not_required',
|
|
add column if not exists security_scanned_at timestamptz,
|
|
add column if not exists security_scan_provider text,
|
|
add column if not exists security_scan_summary jsonb not null default '{}'::jsonb;
|
|
|
|
update public.content_assets
|
|
set security_scan_status = case
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
and upload_status = 'verified'
|
|
then 'passed'
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
and upload_status = 'failed'
|
|
then 'skipped'
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
then 'pending'
|
|
else 'not_required'
|
|
end,
|
|
security_scanned_at = case
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
and upload_status = 'verified'
|
|
then coalesce(security_scanned_at, verified_at, updated_at, created_at, now())
|
|
else security_scanned_at
|
|
end,
|
|
security_scan_provider = case
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
and upload_status = 'verified'
|
|
then coalesce(security_scan_provider, 'legacy_backfill')
|
|
else security_scan_provider
|
|
end,
|
|
security_scan_summary = case
|
|
when storage_provider in ('local_dev', 'supabase_storage', 'aliyun_oss', 'tencent_cos')
|
|
and object_key is not null
|
|
and upload_status = 'verified'
|
|
then coalesce(security_scan_summary, '{}'::jsonb)
|
|
|| jsonb_build_object(
|
|
'riskLevel', 'none',
|
|
'issueCodes', '[]'::jsonb,
|
|
'backfilled', true,
|
|
'backfilledAt', now()
|
|
)
|
|
else coalesce(security_scan_summary, '{}'::jsonb)
|
|
end
|
|
where security_scan_status = 'not_required'
|
|
or security_scan_summary = '{}'::jsonb;
|
|
|
|
do $$
|
|
begin
|
|
if not exists (select 1 from pg_constraint where conname = 'content_assets_security_scan_status_check') then
|
|
alter table public.content_assets
|
|
add constraint content_assets_security_scan_status_check
|
|
check (security_scan_status in ('not_required', 'pending', 'scanning', 'passed', 'failed', 'skipped'));
|
|
end if;
|
|
end $$;
|
|
|
|
create table if not exists public.content_asset_security_scan_events (
|
|
id uuid primary key default gen_random_uuid(),
|
|
tenant_id uuid not null references public.tenants(id) on delete cascade,
|
|
asset_id uuid references public.content_assets(id) on delete set null,
|
|
provider text not null,
|
|
scan_status text not null
|
|
check (scan_status in ('pending', 'scanning', 'passed', 'failed', 'skipped')),
|
|
risk_level text not null default 'none'
|
|
check (risk_level in ('none', 'low', 'medium', 'high', 'critical')),
|
|
issue_codes text[] not null default '{}',
|
|
details jsonb not null default '{}'::jsonb,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
comment on table public.content_asset_security_scan_events is
|
|
'内容资源安全扫描事件。用于记录托管对象的内置规则扫描、杀毒/内容安全 provider 扫描结果、失败原因和审计轨迹。';
|
|
|
|
create index if not exists idx_asset_security_scan_events_asset
|
|
on public.content_asset_security_scan_events(tenant_id, asset_id, created_at desc);
|
|
|
|
create index if not exists idx_asset_security_scan_events_status
|
|
on public.content_asset_security_scan_events(tenant_id, scan_status, risk_level, created_at desc);
|
|
|
|
create index if not exists idx_content_assets_security_scan_status
|
|
on public.content_assets(tenant_id, security_scan_status, upload_status, status, updated_at desc);
|
|
|
|
alter table public.content_asset_security_scan_events enable row level security;
|
|
|
|
drop policy if exists tenant_isolation on public.content_asset_security_scan_events;
|
|
create policy tenant_isolation on public.content_asset_security_scan_events
|
|
for all
|
|
using (tenant_id = app.current_tenant_id() or app.is_platform_admin())
|
|
with check (tenant_id = app.current_tenant_id() or app.is_platform_admin());
|