PageSourceSearch

https://boundaryai.ai/assets/dashboard.learned-rules-CsAjbxEX.js

js boundaryai.ai collected 2026-10-05 13:58:56 UTC 11,925 bytes, 1 lines download raw bytes

1import{a as C,ae as k,b as R,C as Q,f as S,as as H,E as K,r as i,j as e,B as l,at as V,I as U,a5 as W,S as X,i as J,t as x}from"./index-CYfa26JU.js";import{u as P}from"./useMutation-Bf4zUOlf.js";import{D as Y,S as Z,R as ee,E as se,i as z,N as te,f as A}from"./DashboardShell-Cfr9PfYQ.js";import{P as w}from"./charts-CaVztRAM.js";import{B as $}from"./badge-CXhNEDHx.js";import{T as ae}from"./textarea-Doiw-zt5.js";import{S as ne,a as re,b as ie,c as le,d as u}from"./select-BuJdnxg6.js";import{D as de,a as ce,b as oe,c as me,d as xe,e as ue}from"./dialog-BFAWnK7a.js";import{R as he}from"./refresh-cw-DBw7pvJX.js";import{L as I}from"./loader-circle-Dh1Iq8pv.js";import{S as pe}from"./sparkles-rKyhyxf2.js";import{F as ge}from"./funnel-QHO0qSpK.js";import{C as fe}from"./check-pEZzqymH.js";import"./zap-Dd7ru-25.js";import"./wrench-DXXBsi3K.js";import"./users-Dpu3axcO.js";import"./shield-x-C8aC-kD3.js";import"./shield-alert-CzyQPunW.js";import"./shield-CHrIB12_.js";import"./server-QtyMnU9w.js";import"./network-CyY2qIJ7.js";import"./key-round-PrwJCIQY.js";import"./globe-DAiEboTv.js";import"./file-lock-Cy87Hx5b.js";import"./bot-CmphiyPM.js";import"./activity-DLjoFNWa.js";import"./index-Bp1ChL9u.js";import"./triangle-alert-BbQK1IRh.js";import"./index-C4MLugeQ.js";import"./index-BV-3pcbN.js";import"./index-PdFO3pZF.js";const je=C({method:"GET"}).middleware([k("govern")]).handler(R("bc28c4fad8b67fda72423768046c2be9b5a05c14c667603bc0fc1a1fb4a115b4")),ve=C({method:"POST"}).middleware([k("govern")]).handler(R("25818e0a30c8d4df3f2b793dff227fbb5b91173ae7b5214ead25e4d5274d1fe2")),be=C({method:"POST"}).middleware([k("govern")]).handler(R("ad72b85efaa869ef9f4c24ca084214a77e0c9b3e35be2cf57ca8425c3b87db40")),O={pending:"text-amber-400 bg-amber-500/10 ring-amber-500/20",signed:"text-emerald-400 bg-emerald-500/10 ring-emerald-500/20",rejected:"text-rose-400 bg-rose-500/10 ring-rose-500/20"},Ne={pending:"Pending review",signed:"Signed",rejected:"Rejected"},Se={block:"text-rose-400 border-rose-500/30",confirm:"text-amber-400 border-amber-500/30"};function es(){const c=Q(),h=S(je),p=S(ve),q=S(be),F=H("learned_rules"),{data:D,isLoading:_,isError:B,isFetching:T,refetch:L}=K({queryKey:["learned-rules"],queryFn:()=>h(),placeholderData:s=>s,enabled:F===!0}),n=D?.rows??[],[o,G]=i.useState("pending"),[g,M]=i.useState(""),[t,d]=i.useState(null),[E,r]=i.useState(""),f=i.useMemo(()=>{let s=0,a=0,b=0;for(const N of n)N.status==="pending"?s+=1:N.status==="signed"?a+=1:N.status==="rejected"&&(b+=1);return{pending:s,signed:a,rejected:b}},[n]),j=i.useMemo(()=>{const s=g.trim().toLowerCase();return n.filter(a=>!(o!=="all"&&a.status!==o||s&&!`${a.ruleId} ${a.pattern} ${a.actionType??""} ${a.sourceRule??""}`.toLowerCase().includes(s)))},[n,o,g]),v=P({mutationFn:()=>p({data:{windowDays:30}}),onSuccess:s=>{x.success(s.generated>0?`Generated ${s.generated} candidate rule${s.generated===1?"":"s"} from ${s.scanned} incidents`:`Scanned ${s.scanned} incidents — no new generalizable patterns`),c.invalidateQueries({queryKey:["learned-rules"]})},onError:s=>x.error(s?.message??"Scan failed")}),m=P({mutationFn:s=>q({data:{id:s.id,decision:s.verdict,reason:s.reason||null}}),onSuccess:(s,a)=>{x.success(a.verdict==="approved"?"Rule signed into enforcement":"Candidate rejected"),d(null),r(""),c.invalidateQueries({queryKey:["learned-rules"]})},onError:s=>x.error(s?.message??"Decision failed")});return e.jsxs(Y,{children:[e.jsxs("header",{className:"sticky top-0 z-10 border-b border-border/70 bg-background/80 backdrop-blur px-8 h-14 flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex items-baseline gap-3 min-w-0",children:[e.jsx("h1",{className:"text-sm font-semibold shrink-0",children:"Learned rules"}),e.jsx("span",{className:"hidden md:inline text-xs text-muted-foreground truncate",children:"incident → generalized rule → review → sign · the completeness compiler"})]}),F===!0&&e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsx(l,{variant:"outline",size:"sm",className:"h-8",onClick:()=>L(),disabled:T,children:e.jsx(he,{className:`w-3.5 h-3.5 ${T?"animate-spin":""}`})}),e.jsx(l,{size:"sm",className:"h-8",onClick:()=>v.mutate(),disabled:v.isPending,children:v.isPending?e.jsxs(e.Fragment,{children:[e.jsx(I,{className:"mr-1.5 h-3.5 w-3.5 animate-spin"})," Scanning…"]}):e.jsxs(e.Fragment,{children:[e.jsx(pe,{className:"mr-1.5 h-3.5 w-3.5"})," Scan incidents"]})})]})]}),e.jsx(V,{feature:"learned_rules",title:"Learned rules",children:e.jsxs("div",{className:"flex-1 p-8 space-y-6",children:[e.jsxs(w,{accent:"hsl(var(--primary))",title:"Completeness compiler",subtitle:"Every blocked or c
1onfirmed incident is generalized into a candidate rule for review",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[e.jsx(y,{label:"Pending review",value:f.pending,accent:"hsl(var(--warning))"}),e.jsx(y,{label:"Signed",value:f.signed,accent:"hsl(var(--success))"}),e.jsx(y,{label:"Rejected",value:f.rejected,accent:"hsl(var(--destructive))"})]}),e.jsxs("div",{className:"mt-5 pt-4 border-t border-border/50 text-xs text-muted-foreground leading-relaxed",children:["Run-Time Assurance only catches what you modeled. ",e.jsx("strong",{children:"Scan incidents"})," generalizes recent enforced events (by external host, sensitive path, or distinctive phrase) into deterministic candidate rules. Approving one signs it into enforcement so the whole class is caught from then on — a human signs first, nothing auto-enforces."]})]}),e.jsx(w,{className:"p-0",children:e.jsxs("div",{className:"flex items-center gap-2 flex-wrap p-4",children:[e.jsxs("div",{className:"relative flex-1 min-w-[220px]",children:[e.jsx(Z,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground"}),e.jsx(U,{value:g,onChange:s=>M(s.target.value),placeholder:"Search rule id, pattern, action, source…",className:"pl-8 h-9 text-sm"})]}),e.jsxs(ne,{value:o,onValueChange:s=>G(s),children:[e.jsx(re,{className:"w-40 h-9 text-sm",children:e.jsx(ie,{})}),e.jsxs(le,{children:[e.jsx(u,{value:"pending",children:"Pending review"}),e.jsx(u,{value:"signed",children:"Signed"}),e.jsx(u,{value:"rejected",children:"Rejected"}),e.jsx(u,{value:"all",children:"All"})]})]}),e.jsxs("span",{className:"ml-auto text-xs text-muted-foreground tabular-nums",children:[j.length.toLocaleString()," of ",n.length.toLocaleString()," shown"]})]})}),e.jsx(w,{className:"p-0",title:"Candidate rules",subtitle:"Generalized from observed incidents — approve to sign into enforcement, reject to dismiss",children:_&&!D?e.jsx("div",{className:"px-5 py-4",children:e.jsx(ee,{rows:8})}):B?e.jsx(se,{message:"The learned-rules service didn't respond.",onRetry:()=>L()}):n.length===0?e.jsx(z,{icon:e.jsx(te,{className:"w-6 h-6"}),title:"No learned rules yet",hint:"Click Scan incidents to generalize recent blocked / confirmed events into candidate rules for review."}):j.length===0?e.jsx(z,{icon:e.jsx(ge,{className:"w-6 h-6"}),title:"No rules match these filters",hint:"Change the status filter or clear the search."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"bg-muted/40 text-[11px] uppercase tracking-wider text-muted-foreground",children:e.jsxs("tr",{children:[e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Rule"}),e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Action"}),e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Pattern"}),e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Verdict"}),e.jsx("th",{className:"text-right px-4 py-2.5 font-medium",children:"Incidents"}),e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Status"}),e.jsx("th",{className:"text-left px-4 py-2.5 font-medium",children:"Seen"}),e.jsx("th",{className:"text-right px-4 py-2.5 font-medium",children:"Review"})]})}),e.jsx("tbody",{className:"divide-y divide-border/60",children:j.map(s=>e.jsxs("tr",{className:"hover:bg-muted/30 transition-colors",children:[e.jsxs("td",{className:"px-4 py-2.5",children:[e.jsx("div",{className:"font-mono text-xs break-all",children:s.ruleId}),s.sourceRule&&e.jsxs("div",{className:"text-[10px] text-muted-foreground mt-0.5",children:["from ",s.sourceRule]})]}),e.jsx("td",{className:"px-4 py-2.5",children:s.actionType?e.jsx($,{variant:"outline",className:"text-[10px]",children:s.actionType}):e.jsx("span",{className:"text-xs text-muted-foreground",children:"content"})}),e.jsx("td",{className:"px-4 py-2.5 font-mono text-xs text-muted-foreground truncate max-w-[260px]",title:s.pattern,children:s.pattern}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsx($,{variant:"outline",className:`text-[10px] uppercase ${Se[s.decision]??""}`,children:s.decision})}),e.jsx("td",{className:"px-4 py-2.5 text-right tabular-nums text-xs font-medium",children:s.incidentCount}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsx("span",{className:`text-[10px] uppercase font-medium px-2 py-0.5 rounded ring-1 ${O[s.status]??O.pending}`,children:Ne[s.status]??s.status})}),e.jsx("td",{className:"px-4 py-2.5 text-xs text-muted-foreground whitespace-nowrap tabular-nums",title:new Date(s.createdAt).toISOString(),children:A(new Date(s.createdAt),{addSuffix:!0})}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsx("div",{className:"flex justify-end gap-1.5",children:s.status==="pending"?e.jsxs(e.Fragment,{children:[e.jsx(l,{size:"sm",variant:"outline",className:"h-7 px-2 text-xs",onClick:()=>{r(""),d({rule:s,verdict:"approved"})},children:e.jsx(fe,{className:"w-3.5 h-3.5"})}),e.jsx(l,{size:"sm",variant:"ghost",className:"h-7 px-2 text-xs",onClick:()=>{r(""),d({rule:s,verdict:"rejected"})},children:e.jsx(W,{className:"w-3.5 h-3.5"})})]}):e.jsx("span",{className:"text-[10px] text-muted-foreground italic",children:s.reviewedAt?A(new Date(s.reviewedAt),{addSuffix:!0}):"—"})})})]},s.id))})]})})})]})}),e.jsx(de,{open:!!t,onOpenChange:s=>{s||(d(null),r(""))},children:e.jsxs(ce,{children:[e.jsxs(oe,{children:[e.jsx(me,{className:"flex items-center gap-2",children:t?.verdict==="approved"?e.jsxs(e.Fragment,{children:[e.jsx(X,{className:"w-4 h-4 text-emerald-400"})," Sign rule into enforcement"]}):e.jsx(e.Fragment,{children:"Reject candidate"})}),e.jsxs(xe,{children:[e.jsx("span",{className:"font-mono text-xs break-all",children:t?.rule.ruleId}),t?.verdict==="approved"&&e.jsxs("span",{className:"block mt-1 text-xs",children:["Signs and merges this deterministic rule so every future match of",e.jsxs("span",{className:"font-mono",children:[" ",t?.rule.pattern]})," is ",t?.rule.decision,"ed."]})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(J,{htmlFor:"lr-reason",className:"text-xs",children:"Decision reason (optional)"}),e.jsx(ae,{id:"lr-reason",rows:3,value:E,onChange:s=>r(s.target.value),placeholder:"Why are you signing / rejecting this rule?"})]}),e.jsxs(ue,{children:[e.jsx(l,{variant:"ghost",onClick:()=>{d(null),r("")},disabled:m.isPending,children:"Cancel"}),e.jsx(l,{variant:t?.verdict==="rejected"?"destructive":"default",disabled:m.isPending,onClick:()=>t&&m.mutate({id:t.rule.id,verdict:t.verdict,reason:E.trim()}),children:m.isPending?e.jsxs(e.Fragment,{children:[e.jsx(I,{className:"w-3.5 h-3.5 mr-1 animate-spin"})," Submitting…"]}):t?.verdict==="approved"?e.jsx(e.Fragment,{children:"Sign & activate"}):e.jsx(e.Fragment,{children:"Confirm reject"})})]})]})})]})}function y({label:c,value:h,accent:p}){return e.jsxs("div",{className:"rounded-xl border border-border/60 bg-muted/10 p-3",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-[11px] uppercase tracking-wider text-muted-foreground",children:[e.jsx("span",{className:"h-2 w-2 rounded-full",style:{background:p}})," ",c]}),e.jsx("div",{className:"mt-1.5 font-display text-2xl font-bold tabular-nums",children:h.toLocaleStr
1ing()})]})}export{es as component};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.