1import{c as I,u as ue,E as he,r as g,x as me,j as t,S as H,f,B as G,R as K,h as $,o as L,ae as ee,af as Q,ag as pe,ah as fe,ai as xe}from"./index-CKraAXLv.js";import{N as J}from"./navAccents-BZjfB26e.js";import{E as Z}from"./EmptyState-BvWSOwTR.js";import{S as we,a as ge}from"./StickyToolbar-ChuHWzQi.js";import{S as ye,a as je,b as be,c as ve,d as Y}from"./select-BRYILH_h.js";import{s as Ne,k as _e,T as ke,h as Se,i as Ce}from"./datetime-Pe4RNctQ.js";import{u as Me,P as Ae,p as U}from"./ItemViewSheet-Cfrw9qxp.js";import{P as Te}from"./ItemsFormSheet-jEY-0NHn.js";import{P as Ee}from"./PageGlow-lqE2Wv9y.js";import{F as Pe}from"./funnel-MRaBiLOT.js";import{C as De}from"./groups-PDA5OM1A.js";import{C as Ie}from"./clock-DObwKy81.js";import"./useDateFormatter-BGQneRLF.js";import"./index-BMYTV1Nm.js";import"./use-toast-CeTwdgXA.js";import"./alert-dialog-C2ENSZ8t.js";import"./chevron-left-DDugJ9LV.js";import"./pen-line-BjR6CtqA.js";import"./input-Qu_0X-y4.js";import"./DatePicker-BvbpBC32.js";import"./command-Bcgtw9zb.js";const Re=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],Oe=I("copy",Re);const $e=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 9.9-1",key:"1mm8w8"}]],Le=I("lock-open",$e);const Ue=[["path",{d:"M3 7V5a2 2 0 0 1 2-2h2",key:"aa7l1z"}],["path",{d:"M17 3h2a2 2 0 0 1 2 2v2",key:"4qcy5o"}],["path",{d:"M21 17v2a2 2 0 0 1-2 2h-2",key:"6vwrx8"}],["path",{d:"M7 21H5a2 2 0 0 1-2-2v-2",key:"ioqczr"}],["path",{d:"M7 12h10",key:"b7w52i"}]],X=I("scan-line",Ue);const Ve=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2",key:"1yt0o3"}],["path",{d:"M12 18h.01",key:"mhygvu"}]],ze=I("smartphone",Ve);function Be(e){if(!e||!e.trim())return null;try{const r=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`https://${e}`,n=new URL(r).hostname.toLowerCase().replace(/^www\./,"");return n.includes(".")?n:null}catch{return null}}function Fe(e){return typeof e=="string"&&/^http:\/\//i.test(e.trim())}const te=new Set(["password","wifi_password","mail_account","database","server_credential","api_credentials"]);function We(e){return e?te.has(e):!0}function se(e){return!(!e.password||!e.password.trim()||e.type&&!te.has(e.type))}function qe(e){if(!e)return 0;const r=e.length;let n=0;if(/[a-z]/.test(e)&&n++,/[A-Z]/.test(e)&&n++,/[0-9]/.test(e)&&n++,/[^A-Za-z0-9]/.test(e)&&n++,r<6||/^(.)\1+$/.test(e)||/^[0-9]+$/.test(e)&&r<10)return 0;let s=0;return r>=8&&s++,r>=12&&s++,n>=3&&s++,r>=16&&n>=3&&s++,Math.min(4,s)}function He(e,r){return Math.floor((r.getTime()-e.getTime())/(1e3*60*60*24))}function Ge(e){const r=new Map;for(const s of e){if(!se(s))continue;const d=r.get(s.password)??[];d.push(s),r.set(s.password,d)}const n=new Map;for(const[s,d]of r)d.length>1&&n.set(s,d);return n}function V(e){switch(e){case"breached":return 1e3;case"reused":return 100;case"breached_site":return 50;case"insecure":return 20;case"weak":return 10;case"mfa":return 5;case"stale":return 1}}function Ke(e,r=[]){const n=new Map,s=a=>{const i=n.get(a.id);if(i)return i;const l={id:a.id,name:a.name,groupName:a.groupName,issues:[],severity:0};return n.set(a.id,l),l};
1for(const a of e.weak){const i=s(a);i.issues.push("weak"),i.strength=a.strength}for(const a of e.reused){const i=s(a);i.issues.push("reused"),i.reusedWith=a.reusedWith}for(const a of e.stale){const i=s(a);i.issues.push("stale"),i.ageDays=a.ageDays}for(const a of e.insecure)s(a).issues.push("insecure");for(const a of e.mfa)s(a).issues.push("mfa");for(const a of e.breachedSite)s(a).issues.push("breached_site");for(const a of r){const i=s(a);i.issues.push("breached"),i.breachCount=a.breachCount}const d=[...n.values()];for(const a of d)a.issues.sort((i,l)=>V(l)-V(i)),a.severity=a.issues.reduce((i,l)=>i+V(l),0);return d.sort((a,i)=>i.severity-a.severity||a.name.localeCompare(i.name))}function Qe(e,r={}){const n=r.staleDays??365,s=r.weakAtOrBelow??1,d=r.now??new Date,a=e.filter(se),i=[],l=[],k=[],S=[],v=[];for(const c of a){const h={id:c.id,name:c.name,groupName:c.groupName},x=qe(c.password);if(x<=s&&i.push({...h,strength:x}),c.updatedAt){const N=He(Ne(c.updatedAt),d);N>=n&&l.push({...h,ageDays:N})}Fe(c.uri)&&k.push({...h});const T=Be(c.uri);T&&(c.hasTotp===!1&&r.mfaDomains?.has(T)&&S.push({...h}),r.breachedDomains?.has(T)&&v.push({...h}))}const C=[];for(const c of Ge(a).values())for(const h of c)C.push({id:h.id,name:h.name,groupName:h.groupName,reusedWith:c.filter(x=>x.id!==h.id).map(x=>x.id)});const M=a.length,_=new Set([...i.map(c=>c.id),...C.map(c=>c.id),...l.map(c=>c.id),...k.map(c=>c.id),...v.map(c=>c.id)]),A=M===0?100:Math.round((1-_.size/M)*100);return{analysed:M,weak:i,reused:C,stale:l,insecure:k,mfa:S,breachedSite:v,score:A}}const Je="https://api.pwnedpasswords.com/range/";async function Ze(e){const r=new TextEncoder().encode(e),n=await globalThis.crypto.subtle.digest("SHA-1",r);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("").toUpperCase()}function Ye(e,r){const n=r.toUpperCase();for(const s of e.split(/\r?\n/)){const d=s.indexOf(":");if(d!==-1&&s.slice(0,d).toUpperCase()===n){const a=parseInt(s.slice(d+1).trim(),10);return Number.isFinite(a)?a:0}}return 0}async function Xe(e,r=n=>fetch(n)){if(!e)return 0;const n=await Ze(e),s=n.slice(0,5),d=n.slice(5),a=await r(`${Je}${s}`);if(a.ok===!1)throw new Error("HIBP request failed");const i=await a.text();return Ye(i,d)}const et={breached:{icon:xe,chip:"bg-destructive/10 text-destructive"},reused:{icon:Oe,chip:"bg-orange-600/10 text-orange-600 dark:text-orange-400"},breached_site:{icon:fe,chip:"bg-violet-600/10 text-violet-600 dark:text-violet-400"},insecure:{icon:Le,chip:"bg-pink-600/10 text-pink-600 dark:text-pink-400"},weak:{icon:pe,chip:"bg-amber-600/10 text-amber-600 dark:text-amber-400"},mfa:{icon:ze,chip:"bg-blue-600/10 text-blue-600 dark:text-blue-400"},stale:{icon:Ie,chip:"bg-slate-600/10 text-slate-600 dark:text-slate-400"}},z=["breached","reused","breached_site","insecure","weak","mfa","stale"];function B(e){return et[e]}async function tt(e,r){if(!e)return!1;try{let n=JSON.parse(e);const s=n.__encrypted_data__;return typeof s=="string"&&(n=JSON.parse(await ee(s,r))),!!(typeof n.two_factor_secret=="string"&&n.two_factor_secret.trim())}catch{return}}function st(e){return e>=80?{text:"text-success",stroke:"stroke-success"}:e>=50?{text:"text-warning-ink",stroke:"stroke-warning-ink"}:{text:"text-destructive",stroke:"stroke-destructive"}}function at({score:e,tone:r}){const s=2*Math.PI*34;return t.jsxs("div",{className:"relative h-28 w-28 shrink-0",children:[t.jsxs("svg",{viewBox:"0 0 80 80",className:"h-28 w-28 -rotate-90",children:[t.jsx("circle",{cx:"40",cy:"40",r:34,strokeWidth:"7",className:"fill-none stroke-muted"}),t.jsx("circle",{cx:"40",cy:"40",r:34,strokeWidth:"7",strokeLinecap:"round",strokeDasharray:s,strokeDashoffset:s*(1-e/100),className:f("fill-none transition-[stroke-dashoffset] duration-1000 ease-out",r.stroke)})]}),t.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:t.jsx("span",{className:f("font-display text-3xl font-bold tabular-nums",r.text),children:e})})]})}function kt(){const{t:e}=ue(),{encryptionKey:r}=Me(),n=he(),[s,d]=g.useState(!1),[a,i]=g.useState(0),[l,k]=g.useState(null),[S,v]=g.useState(null),[C,M]=g.useState(new Map),[_,A]=g.useState(null),[c,h]=g.useState(null),[x,T]=g.useState(""),[N,ae]=g.useState("all"),{data:re}=me(),F=async()=>
1{if(!r){v(e("watchtower.key_not_ready"));return}d(!0),v(null),i(0);try{const o=(await U.listAll()).filter(m=>We(m.type)),u=new Map;for(const m of o)u.set(m.id,m);const y=await U.scanSecrets(),E=[];for(const[m,j]of y.entries()){const b=u.get(j.id);if(b)try{E.push({id:b.id,name:b.name,type:b.type,password:await ee(j.password,r),updatedAt:b.modified_at,uri:j.uri,hasTotp:await tt(j.details,r)})}catch{}i(Math.round((m+1)/Math.max(y.length,1)*100))}let W,q;try{const[m,j]=await Promise.all([Q(()=>import("./breachedDomains-CFLdxR8T.js"),[]),Q(()=>import("./mfaDomains-BpWfRo0N.js"),[])]);W=new Set(m.default),q=new Set(j.default)}catch{}const w=Qe(E,{breachedDomains:W,mfaDomains:q}),O=[],D=new Map;try{const m=[...new Set(E.map(p=>p.password))],j=8;let b=0;await Promise.all(Array.from({length:Math.min(j,m.length)},async()=>{for(;b<m.length;){const p=m[b++],P=await Xe(p);P>0&&D.set(p,P)}}));for(const p of E){const P=D.get(p.password);P&&O.push({id:p.id,name:p.name,breachCount:P})}try{await U.reportBreaches(E.map(p=>({item_id:p.id,breached:D.has(p.password),occurrences:D.get(p.password)??0}))),n.invalidateQueries({queryKey:["itemBreaches"]})}catch{}}catch{}const de=Ke(w,O);M(u),k({score:w.score,analysed:w.analysed,counts:{weak:w.weak.length,reused:w.reused.length,stale:w.stale.length,insecure:w.insecure.length,mfa:w.mfa.length,breached_site:w.breachedSite.length,breached:O.length},items:de})}catch(o){v(o.message)}finally{d(!1)}},ne=o=>{const u=C.get(o);u&&A(u)},oe=st(l?.score??0),ie=!!re?.is_admin,ce=x.trim().toLowerCase(),R=(l?.items??[]).filter(o=>(N==="all"||o.issues.includes(N))&&o.name.toLowerCase().includes(ce)),le=o=>{switch(o.issues[0]){case"breached":return e("watchtower.breach_count",{count:o.breachCount??0});case"reused":return e("watchtower.reused_with",{count:(o.reusedWith?.length??0)+1});case"weak":return e("watchtower.weak_tag");case"stale":return e("watchtower.age_days",{count:o.ageDays??0});default:return""}};return t.jsxs(_e,{children:[t.jsxs("div",{className:"relative flex-1 p-3 md:p-5",children:[t.jsx(Ee,{}),t.jsxs("div",{className:"space-y-4",children:[t.jsxs("div",{className:"flex flex-col gap-4 md:flex-row md:items-center md:justify-between",children:[t.jsxs("div",{className:"flex items-center gap-4",children:[t.jsx("div",{className:f("rounded-xl p-2.5",J.watchtower.chip),children:t.jsx(H,{className:f("h-8 w-8",J.watchtower.icon)})}),t.jsxs("div",{children:[t.jsx("h1",{className:"flex items-center gap-2.5 font-display text-2xl md:text-3xl font-bold tracking-tight text-foreground",children:e("watchtower.title")}),t.jsx("p",{className:"max-w-2xl text-muted-foreground",children:e(ie?"watchtower.subtitle":"watchtower.subtitle_personal")})]})]}),l&&t.jsxs(G,{onClick:F,variant:"outline",className:"shrink-0",disabled:s,children:[t.jsx(K,{className:f("mr-2 h-4 w-4",s&&"animate-spin")}),e(s?"watchtower.scanning":"watchtower.rescan")]})]}),t.jsx("div",{className:"h-1 overflow-hidden rounded-full bg-primary/10 transition-opacity duration-200",style:{opacity:s?1:0},role:"progressbar","aria-hidden":!s,"aria-label":e("watchtower.scanning_desc"),"aria-valuenow":a,"aria-valuemin":0,"aria-valuemax":100,children:t.jsx("div",{className:"h-full rounded-full bg-primary transition-[width] duration-300 ease-out",style:{width:`${a}%`}})}),S&&t.jsx($,{className:f(L,"border-transparent bg-destructive-soft p-4 text-sm text-destructive-ink"),children:S}),!l&&t.jsxs(Z,{className:"py-8 sm:py-16",icon:X,title:e("watchtower.empty_title"),description:e("watchtower.empty_privacy"),children:[t.jsx("div",{className:"mx-auto flex max-w-xl flex-wrap justify-center gap-2",children:z.map(o=>{const u=B(o);return t.jsxs("span",{className:f("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-semibold",u.chip),children:[t.jsx(u.icon,{className:"h-4 w-4"}),e(`watchtower.${o}`)]},o)})}),t.jsxs(G,{size:"lg",className:"mt-6 sm:mt-8",onClick:F,disabled:s,children:[s?t.jsx(K,{className:"mr-2 h-4 w-4 animate-spin"}):t.jsx(X,{className:"mr-2 h-4 w-4"}),e(s?"watchtower.scanning":"watchtower.run_scan")]})]}),l&&t.jsxs(t.Fragment,{children:[t.jsxs($,{className:f(L,"flex flex-col items-center gap-6 p-6 md:flex-row md:items-center md:gap-8"),children:[t.jsxs("div",{className:"flex items-center gap-5 md:border-r md:border-border/50 md:pr-8",children:[t.jsx(at,{score:l.score,tone:oe}),t.jsxs("div",{children:[t.jsx("div",{className:"text-sm font-semibold text-foreground",children:e("watchtower.posture_score")}),t.jsx("div",{className:"mt-0.5 text-xs text-muted-foreground",children:e("watchtower.analysed",{count:l.anal
1ysed})})]})]}),t.jsx("div",{className:"grid w-full flex-1 grid-cols-2 gap-4 sm:grid-cols-4",children:z.map(o=>{const u=B(o),y=l.counts[o];return t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("span",{className:f("flex h-10 w-10 shrink-0 items-center justify-center rounded-xl",y>0?u.chip:"bg-muted text-muted-foreground/50"),children:t.jsx(u.icon,{className:"h-5 w-5"})}),t.jsxs("div",{className:"min-w-0",children:[t.jsx("div",{className:"font-display text-2xl font-bold tabular-nums leading-none",children:y}),t.jsx("div",{className:"mt-1 truncate text-[11px] text-muted-foreground",children:e(`watchtower.${o}`)})]})]},o)})})]}),l.items.length===0?t.jsx(Z,{className:"py-14",icon:H,title:e("watchtower.all_clear_title"),description:e("watchtower.all_clear_desc")}):t.jsxs(t.Fragment,{children:[t.jsx(we,{children:t.jsxs("div",{className:"flex items-center gap-2 md:gap-3",children:[t.jsx(ge,{value:x,onChange:T,placeholder:e("watchtower.search_placeholder"),containerClassName:"flex-1"}),t.jsxs(ye,{value:N,onValueChange:o=>ae(o),children:[t.jsx(je,{className:"h-11 w-auto shrink-0 px-3.5 font-medium sm:w-60",children:t.jsxs("div",{className:"flex min-w-0 items-center gap-2.5",children:[t.jsx(Pe,{className:f("h-4 w-4 shrink-0",N==="all"?"text-muted-foreground":"text-primary-emphasis dark:text-primary")}),t.jsx("span",{className:"hidden min-w-0 sm:block",children:t.jsx(be,{})})]})}),t.jsxs(ve,{children:[t.jsx(Y,{value:"all",children:t.jsx("span",{className:"text-muted-foreground",children:e("watchtower.all_problems")})}),z.map(o=>t.jsx(Y,{value:o,children:e(`watchtower.${o}`)},o))]})]})]})}),t.jsxs($,{className:L,children:[t.jsx("div",{className:"border-b border-border/50 px-4 py-3 text-sm font-semibold",children:e("watchtower.issues_found",{count:R.length})}),R.length===0?t.jsx("p",{className:"px-4 py-10 text-center text-sm text-muted-foreground",children:e("watchtower.no_matches")}):t.jsx("div",{className:"divide-y divide-border/50",children:R.map(o=>t.jsxs("button",{type:"button",onClick:()=>ne(o.id),className:"group flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted",children:[t.jsxs("div",{className:"min-w-0 flex-1",children:[t.jsx("p",{className:"truncate text-sm font-medium text-foreground",children:o.name}),t.jsx("p",{className:"truncate text-xs text-muted-foreground",children:le(o)})]}),t.jsx("div",{className:"flex shrink-0 items-center gap-1.5",children:o.issues.map(u=>{const y=B(u);return t.jsxs(ke,{children:[t.jsx(Se,{asChild:!0,children:t.jsx("span",{className:f("flex h-7 w-7 items-center justify-center rounded-lg",y.chip),children:t.jsx(y.icon,{className:"h-4 w-4"})})}),t.jsx(Ce,{children:e(`watchtower.${u}`)})]},u)})}),t.jsx(De,{className:"h-4 w-4 shrink-0 text-muted-foreground/40 transition-transform group-hover:translate-x-0.5"})]},o.id))})]})]})]})]})]}),_&&t.jsx(Ae,{groupId:_.group_id??0,entry:_,onClose:()=>A(null),onEdit:()=>{const o=_;A(null),h(o)}}),c&&t.jsx(Te,{groupId:c.group_id??0,initial:c,onClose:()=>h(null),onSaved:()=>{h(null),n.invalidateQueries({queryKey:["allPasswords"]})}})]})}export{kt as WatchtowerPage};
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.