vibemole 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +8 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,12 +11,13 @@ ${o??`CLI auth start failed (${e}).`}`}function mn(e){return La("sha256").update
|
|
|
11
11
|
`).replace(/\n{3,}/g,`
|
|
12
12
|
|
|
13
13
|
`).trim().slice(0,t)}function ji(e,t=4e3){return(e.includes("<")?st(e,t):e).replace(/\s+/g," ").trim().slice(0,t)}function zi(e,t,o){if(t){let a=e.match(new RegExp(`<([a-z0-9]+)[^>]*\\sid=["']${Si(t)}["'][^>]*>([\\s\\S]*?)(?=<h[1-6]\\b|<section\\b|</body)`,"i"));if(a?.[2])return a[2]}if(o==="cookie"){let a=e.match(/<h[1-6][^>]*>[^<]*\bcookies?\b[^<]*<\/h[1-6]>([\s\S]*?)(?=<h[1-6]\b|$)/i);if(a?.[1])return a[1]}return null}function Li(e,t,o,a=4e3){let n=zi(e,t,o);if(n){let i=st(n,a);if(i.length>(o==="cookie"?40:80))return i}return st(e,a)}function Ui(e,t){let o=e.toLowerCase(),a=/(privacy policy|cookie policy|cookie notice|terms of service|terms and conditions|data protection|политика конфиденциальности|условия использования|политика cookie)/i.test(e),n=/(personal data|cookies?|analytics|tracking|processor|provider|third party|retention|legal basis|privacy|персональные данные|контролер данных|поставщик|область действия|куки|файлы cookie)/i.test(e);return/(sign in|log in|login|email password|forgot password|create account|войти|авториза)/i.test(e)&&!a?!1:t==="privacy"?a||/политика конфиденциальности|персональные данные|контролер данных/i.test(e)||o.includes("privacy")&&n:t==="cookie"?a||/куки|файлы cookie/i.test(e)||(o.includes("cookie")||o.includes("cookies"))&&n:t==="terms"?a||/условия|поставщик|область действия/i.test(e)||o.includes("terms"):a&&n}function Ii(e,t){if(!t)return!1;let o=new URL(t),a=new URL(e);return o.pathname===a.pathname&&!o.searchParams.has("next")?!1:/(login|signin|sign-in|auth)/i.test(o.pathname)||o.searchParams.has("next")||o.searchParams.has("redirect")}function Mi(e){return/posthog/i.test(e)?"PostHog":/supabase/i.test(e)?"Supabase":/^openai$|@openai/i.test(e)?"OpenAI":/stripe/i.test(e)?"Stripe":e}function rt(e,t){let o=ct(t),a=e.toLowerCase(),n={},i=o.includes("PostHog");for(let r of o){if(r==="PostHog"&&i){n[r]=a.includes("posthog")||/analytics|product analytics|usage analytics|tracking|cookies?/i.test(a);continue}n[r]=a.includes(r.toLowerCase())}return n}async function An(e,t){let o=e,a=t.typeGuess??Rn(e),{fetchUrl:n,fragment:i}=Pi(e),r=Pn(n,t.baseUrl);if(!r)return{url:o,status:null,finalUrl:null,fetched:!1,extractionSucceeded:!1,normalizedTextExcerpt:"",mentions:rt("",t.observedTerms??[]),contentLooksLikePolicy:!1,error:"policy URL is not same-origin"};try{let s=t.fetchImpl??fetch,c=t.textCap??4e3,l={};t.acceptLanguage&&(l["Accept-Language"]=t.acceptLanguage);let u=await s(r,Object.keys(l).length>0?{headers:l}:void 0),d=await u.text(),m=u.url||r,g=Ii(o,m),y=Li(d,i,a,c),v=ji(y,c),b=!g&&Ui(v,a),x=v.length>0&&b;return{url:o,status:u.status,finalUrl:m,fetched:!0,extractionSucceeded:x,plainTextExcerpt:y,normalizedTextExcerpt:v,mentions:rt(v,t.observedTerms??[]),contentLooksLikePolicy:b,...g?{redirectedFromCandidate:!0}:{},...x?{}:{error:g?"redirected to login or protected page; policy text not accessible":v.length===0?"no policy text extracted":"fetched content does not look like policy text"}}}catch(s){return{url:o,status:null,finalUrl:null,fetched:!1,extractionSucceeded:!1,normalizedTextExcerpt:"",mentions:rt("",t.observedTerms??[]),contentLooksLikePolicy:!1,error:s instanceof Error?s.message:String(s)}}}function Di(e){return{...e,cookies:e.cookies.map(({name:t,domain:o,path:a,expiry:n,isSession:i,secure:r,httpOnly:s,sameSite:c})=>{let l={name:t,domain:o,path:a};return n!==void 0&&(l.expiry=n),i!==void 0&&(l.isSession=i),r!==void 0&&(l.secure=r),s!==void 0&&(l.httpOnly=s),c!==void 0&&(l.sameSite=c),l}),storage:e.storage.map(({type:t,key:o})=>({type:t,key:o}))}}function Oi(e){let t=0,o={};for(let[a,n]of Object.entries(e))/authorization|cookie|set-cookie|token|secret|api[-_]?key/i.test(a)&&n?(o[a]=null,t+=1):o[a]=n;return{headers:o,stripped:t}}function jn(e,t={}){let o=t.policyTextCap??e.redactionSummary.policyTextCappedToChars??4e3,a=0,n=0,i=0,r=e.runtimeProfiles.map(s=>{a+=s.evidence.cookies.filter(y=>y.value!==void 0).length,n+=s.evidence.storage.filter(y=>y.value!==void 0).length;let c=Di(s.evidence),l=Oi(s.evidence.securityHeaders);i+=l.stripped;let{loadedScriptUrls:u,resourceUrls:d,pageSiteSignals:m,...g}=c;return{...s,evidence:{...g,...u?{loadedScriptUrls:u}:{},...d?{resourceUrls:d}:{},...m?{pageSiteSignals:m}:{},securityHeaders:l.headers}}});return{...e,runtimeProfiles:r,...e.runtimeConsentDiffs?{runtimeConsentDiffs:e.runtimeConsentDiffs}:{},policyEvidence:{candidates:e.policyEvidence.candidates.map(s=>({...s,linkTextSnippet:s.linkTextSnippet?.slice(0,160)})),fetched:e.policyEvidence.fetched.map(s=>({...s,normalizedTextExcerpt:s.normalizedTextExcerpt.slice(0,o),...s.plainTextExcerpt?{plainTextExcerpt:s.plainTextExcerpt.slice(0,o)}:{}}))},redactionSummary:{...e.redactionSummary,cookieValuesStripped:e.redactionSummary.cookieValuesStripped+a,storageValuesStripped:e.redactionSummary.storageValuesStripped+n,authTokensStripped:e.redactionSummary.authTokensStripped+i,rawHtmlStripped:!0,inlineScriptsStripped:!0,policyTextCappedToChars:o}}}function dt(e){return{name:e.name,domain:e.domain,path:e.path}}function mt(e){return{type:e.type,key:e.key}}function zn(e){return`${e.name}|${e.domain}|${e.path}`}function Ln(e){return`${e.type}|${e.key}`}function N(e,t,o){let a=new Map(e.map(c=>[o(c),c])),n=new Map(t.map(c=>[o(c),c])),i=t.filter(c=>!a.has(o(c))),r=e.filter(c=>!n.has(o(c))),s=e.filter(c=>n.has(o(c)));return{added:i,removed:r,unchanged:s}}function Un(e){let t=new Map;for(let a of e){let n=`${a.routeUrl}::${a.authenticated?"auth":"anon"}`,i=t.get(n)??[];i.push(a),t.set(n,i)}let o=[];for(let a of t.values()){let n=a[0]?.routeUrl,i=a[0]?.authenticated??!1;if(!n)continue;let r=a.find(m=>m.consentMode==="none");if(!r?.evidence.consent.bannerFound)continue;let s=a.find(m=>m.consentMode==="accepted"),c=a.find(m=>m.consentMode==="declined"),l=[],u=r&&r.evidence.navigationSucceeded?{cookies:r.evidence.cookies.map(dt),storage:r.evidence.storage.map(mt)}:void 0;u||l.push("beforeConsent: baseline none profile missing or navigation failed"),s?s.consentInteraction.succeeded||l.push(`afterAccept: consent interaction incomplete (${s.consentInteraction.error??"no accept control"})`):l.push("afterAccept: accepted profile was not collected"),c?c.consentInteraction.succeeded||l.push(`afterDecline: consent interaction incomplete (${c.consentInteraction.error??"no reject control"})`):l.push("afterDecline: declined profile was not collected");let d={routeUrl:n,authenticated:i,availablePhases:{beforeConsent:!!u,afterAccept:!!s?.consentInteraction.succeeded,afterDecline:!!c?.consentInteraction.succeeded},...u?{beforeConsent:u}:{},...l.length>0?{missingPhaseReasons:l}:{}};u&&s?.consentInteraction.succeeded&&(d.afterAccept={cookies:N(u.cookies,s.evidence.cookies.map(dt),zn),storage:N(u.storage,s.evidence.storage.map(mt),Ln),requestDomains:N(r.evidence.requestDomains,s.evidence.requestDomains,m=>m),trackerDomains:N(r.evidence.trackerDomains,s.evidence.trackerDomains,m=>m)}),u&&c?.consentInteraction.succeeded&&(d.afterDecline={cookies:N(u.cookies,c.evidence.cookies.map(dt),zn),storage:N(u.storage,c.evidence.storage.map(mt),Ln),requestDomains:N(r.evidence.requestDomains,c.evidence.requestDomains,m=>m),trackerDomains:N(r.evidence.trackerDomains,c.evidence.trackerDomains,m=>m)}),(u||s||c)&&o.push(d)}return o}var Re={privacy:24e3,cookie:8e3,terms:8e3,unknown:8e3},Mn={"project-route":0,"rendered-link":1,"project-trace":2,"route-scan":3,"sitemap-like-route":4};function pt(e,t){return new URL(t,e).toString()}function qi(e){return Re[e.typeGuess]??Re.unknown}function Fi(e){try{let t=new URL(e).pathname.toLowerCase();return t==="/ru"||t.startsWith("/ru/")?"ru":t==="/en"||t.startsWith("/en/")?"en":/^\/[a-z]{2}(\/|$)/.test(t)?"unknown":"en"}catch{return"unknown"}}function Hi(e){let t=new Map;for(let a of e){let n=t.get(a.typeGuess)??[];n.push(a),t.set(a.typeGuess,n)}let o=[];for(let a of t.values()){let n=a.filter(i=>Fi(i.url)==="en");o.push(...n.length>0?n:a)}return ut(o)}var Vi=/^(@types\/|react$|react-dom$|next$|lucide-react$)/i,Ki=/posthog|supabase|stripe|openai|analytics|cookie|tag manager|gtm/i;function Wi(e){let t=[];for(let o of e)Vi.test(o.name)||(o.category==="analytics"||o.category==="payments"||o.category==="ai"||o.category==="database"||o.category==="consent"||Ki.test(o.name))&&t.push(o.name);return t}function Gi(e,t){let o=new Set(["analytics","cookies","storage","tracking pixels","tag managers"]);for(let a of Wi(e.dependencies))o.add(a);for(let a of t){for(let n of a.evidence.cookies)/^(__ph_|ph_|posthog)/i.test(n.name)&&o.add("PostHog"),/stripe/i.test(n.name)&&o.add("Stripe"),/supabase/i.test(n.name)&&o.add("Supabase");for(let n of a.evidence.storage)/^(__ph_|ph_|posthog)/i.test(n.key)&&o.add("PostHog"),/stripe/i.test(n.key)&&o.add("Stripe"),/supabase/i.test(n.key)&&o.add("Supabase"),/openai/i.test(n.key)&&o.add("OpenAI");for(let n of a.evidence.knownGlobals??[])/^(dataLayer|google_tag_manager|gtag|ga)$/i.test(n)&&o.add("Google Tag Manager"),/^(fbq|ttq|hj|clarity|ym|analytics|amplitude)$/i.test(n)&&o.add(n),/posthog/i.test(n)&&o.add("PostHog");for(let n of a.evidence.trackerDomains)/posthog|whatsup\.paradaq|phc_/i.test(n)?o.add("PostHog"):/stripe/i.test(n)?o.add("Stripe"):/supabase/i.test(n)?o.add("Supabase"):/openai/i.test(n)&&o.add("OpenAI");for(let n of[...a.evidence.requestDomains,...a.evidence.resourceDomains,...a.evidence.scriptDomains])/posthog|whatsup\.paradaq|phc_/i.test(n)?o.add("PostHog"):/stripe/i.test(n)?o.add("Stripe"):/supabase/i.test(n)?o.add("Supabase"):/openai/i.test(n)&&o.add("OpenAI");for(let n of a.evidence.loadedScriptUrls??[])/\/array\/phc_|posthog/i.test(n)&&o.add("PostHog");for(let n of a.evidence.resourceUrls??[])/\/array\/phc_|posthog/i.test(n)&&o.add("PostHog")}return ct([...o])}function Yi(e){let t=[],o=new Set,a=i=>{let r=lt(i);o.has(r)||(o.add(r),t.push(i))};if(e.targetUrl){if(e.projectEvidence.policyCandidates?.length)for(let i of e.projectEvidence.policyCandidates)a({url:pt(e.targetUrl,i.path),typeGuess:i.typeGuess,provenance:i.provenance,linkTextSnippet:i.textSnippet});else e.projectEvidence.routes.privacy&&a({url:pt(e.targetUrl,"/privacy"),typeGuess:"privacy",provenance:"project-route"}),e.projectEvidence.routes.terms&&a({url:pt(e.targetUrl,"/terms"),typeGuess:"terms",provenance:"project-route"});for(let i of e.runtimeProfiles)for(let r of Tn({sourceRouteUrl:i.routeUrl,links:i.evidence.renderedLinks??[]}))a(r)}let n=ut(t.sort((i,r)=>{let s=Mn[i.provenance]-Mn[r.provenance];return s!==0?s:i.url.localeCompare(r.url)}));return Hi(n)}function Xi(e){return e.some(t=>t.evidence.cookies.length>0||t.evidence.storage.length>0||t.evidence.trackerDomains.length>0)}function Ji(e,t,o,a){let n=[...a],i=e.some(c=>c.typeGuess==="privacy"),r=e.some(c=>c.typeGuess==="cookie");!(i||o.some(c=>(c.evidence.renderedLinks??[]).some(l=>/privacy/i.test(`${l.text} ${l.href}`))))&&e.filter(c=>c.typeGuess==="privacy").length===0?n.push("No privacy policy route or rendered link was found."):i||n.push("No privacy policy candidate was discovered from project routes or rendered links."),Xi(o)&&!r&&n.push("No cookie policy was found although cookies, storage, or tracking signals were observed.");for(let c of t)if(c.fetched){if(c.redirectedFromCandidate){n.push(`Policy fetch for ${c.url} redirected away from the policy page (login or protected route).`);continue}c.fetched&&!c.extractionSucceeded&&(c.contentLooksLikePolicy===!1?n.push(`Policy candidate ${c.url} was fetched but content did not look like policy text.`):c.error&&n.push(`Policy candidate ${c.url}: ${c.error}`))}return[...new Set(n)]}async function Dn(e,t){let o=In.join(e,".vibemole","evidence");await $i(o,{recursive:!0});let a=In.join(o,`${t.scanId}.json`);return await Bi(a,`${JSON.stringify(t,null,2)}
|
|
14
|
-
`,"utf8"),a}async function On(e){let t=e.generatedAt??new Date().toISOString(),o=e.reportLocale??"en",a=ve(o),n=Yi(e),i=Gi(e.projectEvidence,e.runtimeProfiles),r=e.fetchPolicy??((d,m)=>An(d.url,{baseUrl:m.baseUrl,observedTerms:m.observedTerms,textCap:m.textCap,acceptLanguage:m.acceptLanguage,typeGuess:d.typeGuess})),s=e.targetUrl===null?[]:await Promise.all(n.map(d=>r(d,{baseUrl:e.targetUrl,observedTerms:i,textCap:qi(d),acceptLanguage:a}))),c=Ji(n,s,e.runtimeProfiles,e.collectorWarnings??[]),l=Un(e.runtimeProfiles),u={version:Cn,scanId:e.scanId??Ni(),target:{root:e.root,baseUrl:e.targetUrl,generatedAt:t,reportLocale:o},projectEvidence:e.projectEvidence,runtimeProfiles:e.runtimeProfiles,...l.length>0?{runtimeConsentDiffs:l}:{},policyEvidence:{candidates:n,fetched:s},redactionSummary:{cookieValuesStripped:0,storageValuesStripped:0,authTokensStripped:0,rawHtmlStripped:!1,inlineScriptsStripped:!1,policyTextCappedToChars:Re.privacy},collectorDiagnostics:{startedAt:t,finishedAt:new Date().toISOString(),routeCap:e.collectorRouteCap??10,warnings:c}};return jn(u,{policyTextCap:Re.privacy})}var Pe=class{constructor(t,o=!0){this.stream=t;this.enabled=o}stream;enabled;timer=null;frame=0;message="";frames=["|","/","-","\\"];clearLine="\r\x1B[2K";start(t){if(this.enabled){if(this.stop(),this.message=t,!this.stream.isTTY){this.stream.write(`${t}
|
|
14
|
+
`,"utf8"),a}async function On(e){let t=e.generatedAt??new Date().toISOString(),o=e.reportLocale??"en",a=ve(o),n=Yi(e),i=Gi(e.projectEvidence,e.runtimeProfiles),r=e.fetchPolicy??((d,m)=>An(d.url,{baseUrl:m.baseUrl,observedTerms:m.observedTerms,textCap:m.textCap,acceptLanguage:m.acceptLanguage,typeGuess:d.typeGuess})),s=e.targetUrl===null?[]:await Promise.all(n.map(d=>r(d,{baseUrl:e.targetUrl,observedTerms:i,textCap:qi(d),acceptLanguage:a}))),c=Ji(n,s,e.runtimeProfiles,e.collectorWarnings??[]),l=Un(e.runtimeProfiles),u={version:Cn,scanId:e.scanId??Ni(),target:{root:e.root,baseUrl:e.targetUrl,generatedAt:t,reportLocale:o},projectEvidence:e.projectEvidence,runtimeProfiles:e.runtimeProfiles,...l.length>0?{runtimeConsentDiffs:l}:{},policyEvidence:{candidates:n,fetched:s},redactionSummary:{cookieValuesStripped:0,storageValuesStripped:0,authTokensStripped:0,rawHtmlStripped:!1,inlineScriptsStripped:!1,policyTextCappedToChars:Re.privacy},collectorDiagnostics:{startedAt:t,finishedAt:new Date().toISOString(),routeCap:e.collectorRouteCap??10,warnings:c}};return jn(u,{policyTextCap:Re.privacy})}var Pe=class{constructor(t,o=!0){this.stream=t;this.enabled=o}stream;enabled;timer=null;frame=0;message="";runtimeCompleted=0;runtimeTotal=1;runtimeLabel="";frames=["|","/","-","\\"];clearLine="\r\x1B[2K";start(t){if(this.enabled){if(this.stop(),this.message=t,!this.stream.isTTY){this.stream.write(`${t}
|
|
15
15
|
`);return}this.render(),this.timer=setInterval(()=>this.render(),120)}}update(t){if(this.enabled){if(this.message=t,!this.stream.isTTY){this.stream.write(`${t}
|
|
16
|
-
`);return}this.render()}}runtimeProgress(t,o,a){if(
|
|
17
|
-
`);return}this.
|
|
16
|
+
`);return}this.render()}}runtimeProgress(t,o,a){if(this.enabled){if(this.stop(),this.runtimeCompleted=t,this.runtimeTotal=o,this.runtimeLabel=a,!this.stream.isTTY){this.stream.write(`${this.formatRuntimeProgress(t,o,a)}
|
|
17
|
+
`);return}this.renderRuntime(),this.timer=setInterval(()=>this.renderRuntime(),120)}}success(t){this.enabled&&(this.stop(),this.stream.write(this.stream.isTTY?`${this.clearLine}${t}
|
|
18
18
|
`:`${t}
|
|
19
|
-
`))}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}
|
|
19
|
+
`))}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}finishLine(){this.stop(),!(!this.enabled||!this.stream.isTTY)&&(!this.message&&!this.runtimeLabel||(this.stream.write(`${this.clearLine}
|
|
20
|
+
`),this.message="",this.runtimeLabel=""))}render(){let t=this.frames[this.frame%this.frames.length]??"|";this.frame+=1,this.stream.write(`${this.clearLine}${t} ${this.message}`)}renderRuntime(){let t=this.frames[this.frame%this.frames.length]??"|";this.frame+=1;let o=this.formatRuntimeProgress(this.runtimeCompleted,this.runtimeTotal,this.runtimeLabel);this.stream.write(`${this.clearLine}${t} ${o}`)}formatRuntimeProgress(t,o,a){let i=Math.max(o,1),r=Math.min(1,Math.max(0,t/i)),s=Math.min(10,Math.round(r*10));return`${`[${"=".repeat(s)}${" ".repeat(10-s)}]`} ${t}/${o} ${a}`}};var Zi=["No app URL was provided, and no running server could be detected automatically.","VibeMole needs your app running to inspect cookies, storage, and trackers in a live browser.","Without a URL only static project + policy evidence is collected (no runtime scan).","","Start it (e.g. `npm run dev`), then enter its URL below \u2014","or skip and re-run non-interactively with: npx vibemole scan --url http://localhost:3000"].join(`
|
|
20
21
|
`),Qi=["\u26A0 Runtime checks skipped: no running server detected.","VibeMole inspects cookies, storage & trackers in a live browser, so your app must be running.","Start it, then re-run:"," npx vibemole scan --url http://localhost:3000"," (or http://<ip>:<port> for a remote/VPS server)","Continuing with project + policy evidence only\u2026"].join(`
|
|
21
22
|
`),Nn=["Skipping runtime checks; continuing with project + policy evidence only.","For a full scan of your running app, re-run with: npx vibemole scan --url http://localhost:3000"].join(`
|
|
22
23
|
`),er="Server URL [Enter to skip runtime checks]: ",tr="That doesn't look like a valid URL (expected e.g. http://localhost:3000).";function nr(e){let t=e.trim();if(!t)return null;try{let o=new URL(t);return o.protocol!=="http:"&&o.protocol!=="https:"?null:o.toString()}catch{return null}}async function $n(e){if(!e.interactive)return e.log(Qi),null;e.log(Zi);for(let t=0;t<2;t++){let o=await e.ask(er);if(!o.trim())return e.log(Nn),null;let a=nr(o);if(a)return a;e.log(tr)}return e.log(Nn),null}import or from"node:os";function Bn(e){return!e.json&&!e.noUpload}async function qn(e,t={}){let o=t.loadSession??H,a=t.login??Ee,n=t.deviceLabel??`${process.env.USER||"user"}@${or.hostname()}`,i=await o();if(!i||se(i))try{i=await a(e.webUrl,n)}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}let r=xe(i,e);if("error"in r)return{ok:!1,error:r.error};if(se(i))return{ok:!1,error:"CLI session expired. Run: vibemole login"};try{let s=e.uploadUrl??sn(r.webUrl);return e.uploadUrl&&cn(e.uploadUrl,r.webUrl),{ok:!0,uploadUrl:s,session:i}}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}}var Er=ra(Yn(),1);function Te(e,t){return t?{evidencePackage:e,securityEvidence:t}:e}function Xn(e){let t=[`Evidence file: ${e.evidenceFilePath}`];return e.uploadOutcome==="uploaded"&&e.reportUrl&&t.push(`View results: ${e.reportUrl}`),t.join(`
|
|
@@ -2562,5 +2563,6 @@ ${o??`CLI auth start failed (${e}).`}`}function mn(e){return La("sha256").update
|
|
|
2562
2563
|
capabilityIndicators,
|
|
2563
2564
|
policyIndicators,
|
|
2564
2565
|
};
|
|
2565
|
-
})()`}async function xt(){let e=io(import.meta.url),t=Y.dirname(jr(import.meta.url)),o=Y.resolve(t,"../../..");for(let a of[process.cwd(),Y.join(o,"apps","cli"),Y.join(o,"apps","worker"),Y.join(o,"apps","web")])try{return io(Y.join(a,"package.json"))("playwright")}catch{}try{return e("playwright")}catch{return null}}async function zr(e,t,o){return await(await e.chromium.launch(o)).close(),{available:!0,name:t,launchOptions:o}}async function Ue(e={}){let t=e.playwright??await xt(),o=[];if(!t)return{available:!1,installHint:"Run: npx playwright install chromium",attempts:[{name:"playwright-chromium",error:"playwright package is not available"}]};let a=[{name:"chrome",launchOptions:{channel:"chrome",headless:!0}},{name:"msedge",launchOptions:{channel:"msedge",headless:!0}},{name:"playwright-chromium",launchOptions:{headless:!0}}];for(let n of a)try{return await zr(t,n.name,n.launchOptions)}catch(i){o.push({name:n.name,error:i instanceof Error?i.message:String(i)})}return{available:!1,installHint:"Run: npx playwright install chromium",attempts:o}}function wt(e){try{return new URL(e).hostname}catch{return null}}function X(e){return[...new Set(e.filter(t=>!!t))].sort()}function Le(e){return/(posthog|google-analytics|googletagmanager|segment|mixpanel|amplitude|clarity|facebook|doubleclick)/i.test(e)}var Lr=10;function po(e){try{return new URL(e).hostname}catch{return null}}function Ur(e){return e==="local_storage"?"localStorage":e==="session_storage"?"sessionStorage":"indexedDB"}function Ir(e){if(e.key)return e.key;if(e.storageType!=="indexeddb")return null;let t=[e.databaseName,e.objectStoreName,e.collectionStatus].filter(Boolean);return t.length>0?t.join(":"):null}function Mr(e){return Fr(e.flatMap(t=>{let o=Ir(t);return o?[{type:Ur(t.storageType),key:o,value:t.value}]:[]}))}var Dr=200,ro=160,Or=220,Nr=100,$r=100,Br=20;function qr(e){let t=new Set,o=[];for(let a of e){let n=a.text.length>ro?a.text.slice(0,ro):a.text,i=`${a.href}\0${n}`;if(!t.has(i)&&(t.add(i),o.push({...a,text:n}),o.length>=Dr))break}return o}function Fr(e){let t={localStorage:Nr,sessionStorage:$r,indexedDB:Br},o={localStorage:0,sessionStorage:0,indexedDB:0},a=[];for(let n of e)if(!(o[n.type]>=t[n.type])&&(o[n.type]+=1,a.push(n),a.length>=Or))break;return a}function Hr(e){return e.cookies.some(t=>/^(__ph_|ph_|posthog)/i.test(t.name))||e.storage.some(t=>/^(__ph_|ph_|posthog)/i.test(t.key))||e.knownGlobals.some(t=>/posthog/i.test(t))}function St(e){try{let t=new URL(e);return/posthog/i.test(t.hostname)||/\/array\/phc_/i.test(t.pathname)||/\/flags\/?$/i.test(t.pathname)||/\/static\/(?:exception-autocapture|surveys|dead-clicks-autocapture|web-vitals)\.js$/i.test(t.pathname)}catch{return!1}}function so(e){let t=new Set(e.trackerDomains.map(a=>a.toLowerCase()));return X(e.urls).filter(a=>{let n=po(a)?.toLowerCase();return n?t.has(n)||Le(n)||St(a):!1}).slice(0,e.limit??25)}function Vr(e){let t=new Set(e.domains.filter(Le)),o=Hr(e);for(let a of e.urls){let n=po(a);n&&(Le(n)||St(a)||o&&St(a))&&t.add(n)}if(e.knownGlobals.some(a=>/^(dataLayer|google_tag_manager|gtag|ga|fbq|ttq|hj|clarity|ym|analytics|amplitude)$/i.test(a)))for(let a of e.domains.filter(Le))t.add(a);return[...t].sort()}var co=/(?:^|\/)(?:privacy|cookie|cookies|legal|terms|gdpr|impressum|data-protection|datenschutz)(?:\/|$)/i;function Kr(e){return!e||e==="/"?"/":e.startsWith("/")?e:`/${e}`}function go(e){let t=new Set(["/"]);for(let i of e)t.add(Kr(i));let o=[...t],a=o.filter(i=>i!=="/"&&co.test(i)).sort(),n=o.filter(i=>i!=="/"&&!co.test(i)).sort();return["/",...a,...n]}function Wr(e,t,o){let a=new Set;return t.map(n=>{try{return new URL(n,e).toString()}catch{return null}}).filter(n=>!!n).filter(n=>a.has(n)?!1:(a.add(n),!0)).slice(0,o)}function fo(e){let t=e.routeCap??8,o=e.routes?.length?e.routes:["/"],a=e.consentModes??["none","accepted","declined"];return Wr(e.targetUrl,o,t).flatMap(i=>a.map(r=>({routeUrl:i,consentMode:r,authenticated:e.authenticated})))}function ho(e){return fo({...e,consentModes:["none"]})}function yo(e){return fo({targetUrl:e.targetUrl,routes:[e.routeUrl],authenticated:e.authenticated,routeCap:1,consentModes:["accepted","declined"]})}async function J(e,t){return e.evaluate(t)}async function ue(e,t){await new Promise(o=>setTimeout(o,t))}async function Gr(e,t){let o=e.waitForLoadState?.("networkidle",{timeout:t});o&&await o.catch(()=>{})}var Yr=2e3,Xr=100;function lo(e){return e.metaDescription?e.faviconFromExplicitLink!==!0:!0}async function Jr(e){let t=await J(e,vt());if(!lo(t))return t;let o=Date.now()+Yr;for(;Date.now()<o&&(await ue(e,Xr),t=await J(e,vt()),!!lo(t)););return t}async function uo(e){return J(e,bt({checkBanner:!0,acceptedModeKeywords:Ae,declinedModeKeywords:je,settingsModeKeywords:gt}))}function mo(e){return e.consentBannerFound==="yes"&&q(e)}async function Zr(e){let t=await uo(e);if(!mo(t)){for(let o of ft)if(await ue(e,o),t=await uo(e),mo(t))break}return t}async function bo(e){let t=await xt();if(!t)return ze("playwright package is not available");let o=e.browserRuntime??await Ue({playwright:t});if(!o.available)return ze("browser runtime is not available");let a=await t.chromium.launch(o.launchOptions);try{let n=await xt();if(await a.close(),!n)return ze("playwright package is not available");let i=await n.chromium.launch(o.launchOptions);try{let r=await i.newContext({storageState:e.storageState,...e.acceptLanguage?{extraHTTPHeaders:{"Accept-Language":e.acceptLanguage}}:{}}),s=await r.newPage(),c=[],l={};s.on("request",h=>{c.push(h.url())});let u=await s.goto(e.targetUrl,{waitUntil:"load",timeout:e.timeoutMs??15e3});await Gr(s,yt);let d=e.consentMode==="none"?1500:1200;await ue(s,d);let m=await Zr(s),g=to(m),y=m.consentBannerSelectorForCapture,v={attempted:!1,succeeded:!0,action:"none"};if(e.consentMode&&e.consentMode!=="none"){let h=await J(s,kt({mode:e.consentMode,acceptedModeKeywords:Ae,declinedModeKeywords:je,bannerRootSelector:y}));v=no(e.consentMode,h,m),(h.consentOutcome==="clicked_accept"||h.consentOutcome==="clicked_decline")&&(await ue(s,700),await ue(s,ht),await s.waitForLoadState?.("networkidle",{timeout:5e3}).catch(()=>{}))}let b=u?.headers?.()??{};for(let h of["content-security-policy","strict-transport-security","x-frame-options","x-content-type-options","referrer-policy"])l[h]=b[h]??null;let x=await s.title().catch(()=>null),f=x&&x.trim()?x.trim():null,w=await J(s,oo(Lr)),P=Mr(w),j=qr(await s.evaluate(()=>Array.from(document.querySelectorAll("a[href]")).map((h,z)=>{let R=h;return{href:R.href||R.getAttribute("href")||"",text:(R.textContent||R.getAttribute("aria-label")||"").replace(/\s+/g," ").trim(),selector:`a[href]:nth-of-type(${z+1})`}}))),T=await r.cookies(),_=await J(s,ao()),C=await Jr(s),$=X([...c,..._.resourceUrls,..._.loadedScriptUrls]),U=X(c.map(wt)),ge=X([...c.filter(h=>/\.(js|mjs)(\?|$)/.test(h)),..._.loadedScriptUrls].map(wt)),te=X($.map(wt)),A=Vr({domains:te,urls:$,cookies:T,storage:P,knownGlobals:_.knownGlobals}),I=await Qr(s,!!e.includeAccessibility);return await r.close(),{characterization:m,evidence:{navigationSucceeded:!0,finalUrl:s.url(),status:u?.status?.()??null,title:f,securityHeaders:l,cookies:T.map(h=>{let z=h.expires,R=Number.isFinite(z)&&(z??0)>0;return{name:h.name,value:h.value,domain:h.domain,path:h.path,expiry:R?new Date(z*1e3).toISOString():null,isSession:!R,secure:h.secure,httpOnly:h.httpOnly,sameSite:h.sameSite}}),storage:P,requestDomains:U,scriptDomains:ge,resourceDomains:te,trackerDomains:A,loadedScriptUrls:so({urls:_.loadedScriptUrls,trackerDomains:A}),resourceUrls:so({urls:_.resourceUrls,trackerDomains:A}),pageSiteSignals:{faviconUrl:C.faviconUrl??null,faviconFromExplicitLink:C.faviconFromExplicitLink===!0,metaDescription:C.metaDescription??null,canonicalUrl:C.canonicalUrl??null,htmlLang:C.htmlLang??null,policyLinkUrls:X((C.policyIndicators??[]).map(h=>h.url).filter(h=>typeof h=="string"&&h.length>0))},knownGlobals:_.knownGlobals,renderedLinks:j,consent:g,accessibility:I},consentInteraction:v}}finally{await i.close()}}catch(n){return ze(n instanceof Error?n.message:String(n))}}async function Qr(e,t){if(!t)return{scanned:!1,violations:[],skippedReason:"accessibility scan disabled"};try{let a=await new Function("specifier","return import(specifier)")("@axe-core/playwright"),n=a.default??a.AxeBuilder;return{scanned:!0,violations:(await new n({page:e}).analyze()).violations.map(r=>({id:String(r.id),impact:r.impact?String(r.impact):null,description:String(r.description),nodes:Array.isArray(r.nodes)?r.nodes.length:0}))}}catch(o){return{scanned:!1,violations:[],skippedReason:o instanceof Error?o.message:"axe scan unavailable"}}}function es(e){return{navigationSucceeded:!1,finalUrl:null,status:null,title:null,securityHeaders:{},cookies:[],storage:[],requestDomains:[],scriptDomains:[],resourceDomains:[],trackerDomains:[],consent:{bannerFound:!1,acceptPresent:!1,rejectPresent:!1,settingsPresent:!1,detectionConfidence:"low",detectionBasis:e},accessibility:{scanned:!1,violations:[],skippedReason:e},skippedReason:e}}function ze(e){return{characterization:{consentBannerFound:"no",consentDetectionConfidence:"low",consentBannerDetectionBasis:e,consentCaptureQualityClass:"unusable",consentButtons:{acceptPresent:!1,declinePresent:!1,settingsPresent:!1},consentBannerSelectorForCapture:null,consentUx:null},evidence:es(e),consentInteraction:{attempted:!1,succeeded:!1,action:"none",error:e}}}var xo="Scanning project evidence...",So="Project evidence scanned.",Eo="Building redacted evidence package...",Co="Redacted evidence package built.",_o=10,ts=2;async function Et(e,t,o,a){t&&e.start(o.start),await a(),t&&e.success(o.success)}async function ns(e,t,o,a=null){let n={"content-type":"application/json"};o&&(n.authorization=`Bearer ${o}`);let i=Te(t,a),r=await fetch(e,{method:"POST",headers:n,body:JSON.stringify(i)});if(!r.ok){let s=await r.text().catch(()=>"");throw new Error(`Upload failed with ${r.status}${s?`: ${s.slice(0,240)}`:""}`)}return await r.json().catch(()=>({}))}async function Ro(e){if(!(e.forceUpload??Bn(e.args)))return{exitCode:0};let o=e.sink??{log:r=>console.log(r),error:r=>console.error(r)},a=e.upload??ns,n=e.poll??Jn,i=await qn(e.args,e.prepareDeps);if(!i.ok)return o.error(i.error),{exitCode:1};e.progress.start("Uploading redacted evidence package...");try{let r=await a(i.uploadUrl,e.evidencePackage,i.session.token,e.securityEvidence??null);e.progress.success("Upload succeeded.");let s=r.scanId??e.evidencePackage.scanId,c=i.session.webUrl;if(r.analysisStatus==="stored_only")return o.log(r.message??"Evidence stored. Report analysis is not generated yet."),{exitCode:0,scanId:s,reportUrl:r.reportUrl?G(c,s,r.reportUrl):void 0};if(r.analysisStatus==="pending"&&r.statusUrl){e.progress.start("Waiting for security grading...");let l=await n({statusUrl:r.statusUrl,bearerToken:i.session.token,webUrl:c,scanId:s},e.pollDeps);return e.progress.stop(),l.outcome==="ready"?{exitCode:0,scanId:s,reportUrl:G(c,s,l.reportUrl)}:l.outcome==="failed"?(o.error(l.error??"Security grading failed."),{exitCode:1,scanId:s}):{exitCode:0,scanId:s,reportUrl:G(c,s,l.reportUrl)}}return r.reportUrl?{exitCode:0,scanId:s,reportUrl:G(c,s,r.reportUrl)}:{exitCode:0,scanId:s,reportUrl:G(c,s)}}catch(r){return e.progress.stop(),o.error(r instanceof Error?r.message:String(r)),{exitCode:1}}}function ko(e){if(!e||e==="/")return"/";let t=e.trim();return(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"")||"/"}function os(e){return`Runtime baseline routes: ${e.join(", ")}`}function as(e,t=_o,o=ts){let a=new Set(["/"]);for(let n of e.runtimeRouteCandidates??[])a.add(ko(n));for(let n of e.policyCandidates??[])a.add(ko(n.path));return go([...a]).slice(0,t)}function vo(e){return`Collecting ${new URL(e.routeUrl).pathname} (${e.consentMode})`}function wo(e,t){let o=new URL(e.routeUrl).pathname;return{profileId:`${e.authenticated?"authenticated":"anonymous"}:${o}:${e.consentMode}`,routeUrl:e.routeUrl,consentMode:e.consentMode,authenticated:e.authenticated,evidence:t.evidence,consentInteraction:t.consentInteraction}}async function Po(e){let t=e.collect??bo,o=e.routeCap??_o,a=as(e.projectEvidence,o,e.pagesPerLevel);e.verbose&&(e.log??(l=>console.log(l)))(os(a));let n=ho({targetUrl:e.targetUrl,routes:a,authenticated:e.authenticated,routeCap:o}),i=[],r=[],s=[];for(let[c,l]of n.entries()){e.progress.runtimeProgress(c+1,n.length,vo(l));let u=await t({targetUrl:l.routeUrl,storageState:e.storageState,browserRuntime:e.browserRuntime,consentMode:l.consentMode,includeAccessibility:!1,...e.acceptLanguage?{acceptLanguage:e.acceptLanguage}:{}});if(r.push(wo(l,u)),q(u.characterization)){let d=new URL(l.routeUrl).pathname;u.evidence.consent.acceptPresent||s.push(`Consent banner on ${d} has no accept control; accept phase may be incomplete`),u.evidence.consent.rejectPresent||s.push(`Consent banner on ${d} has no reject control; decline phase may be incomplete`),i.push(...yo({targetUrl:e.targetUrl,routeUrl:l.routeUrl,authenticated:e.authenticated}))}else if(u.evidence.consent.bannerFound){let d=new URL(l.routeUrl).pathname;s.push(`Skipped consent variants for ${d}: banner detection was too weak to interact safely`)}else{let d=new URL(l.routeUrl).pathname;s.push(`Skipped consent variants for ${d}: no consent banner found`)}}for(let[c,l]of i.entries()){e.progress.runtimeProgress(c+1,i.length,vo(l));let u=await t({targetUrl:l.routeUrl,storageState:e.storageState,browserRuntime:e.browserRuntime,consentMode:l.consentMode,includeAccessibility:!1,...e.acceptLanguage?{acceptLanguage:e.acceptLanguage}:{}});r.push(wo(l,u))}return e.progress.success(`Runtime evidence collected (${r.length} profiles).`),{profiles:r,warnings:s}}var Ie=512e3;import{readdir as is,readFile as To,stat as rs}from"node:fs/promises";import Me from"node:path";var ss=new Set(["node_modules",".next","dist","build","coverage","out",".git",".vibemole",".cache",".turbo"]),Ao=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".yml",".yaml",".toml",".sql",".rules",".env",".local"]),jo=new Set([".env",".env.local",".env.development",".env.production",".env.test",".env.example",".env.sample",".gitignore",".dockerignore","Dockerfile","docker-compose.yml","docker-compose.yaml","firebase.json","firestore.rules","storage.rules","package.json","package-lock.json","pnpm-lock.yaml","yarn.lock","bun.lockb","next.config.js","next.config.mjs","next.config.ts"]),cs=new Set([".env",".env.local",".env.development",".env.production",".env.test",".env.example",".env.sample"]);function ls(e,t){let o=e.replace(/\\/g,"/");if(cs.has(t)||t.startsWith(".env."))return"env";if(t==="Dockerfile"||t.startsWith("docker-compose")||o.startsWith(".github/workflows/")&&(t.endsWith(".yml")||t.endsWith(".yaml")))return"devops";if(o.startsWith("supabase/")&&t.endsWith(".sql"))return"sql";if(t.endsWith(".rules")||t==="firebase.json")return"rules";if(t==="package.json"||t==="package-lock.json"||t==="pnpm-lock.yaml"||t==="yarn.lock"||t==="bun.lockb")return"manifest";if(t===".gitignore"||t===".dockerignore"||t.startsWith("next.config."))return"config";let a=Me.extname(t);return Ao.has(a)?a===".sql"?"sql":a===".json"||a===".yml"||a===".yaml"||a===".toml"?"config":"source":jo.has(t)?t==="Dockerfile"||t.startsWith("docker-compose")?"devops":"config":null}function us(e,t){if(jo.has(t)||t.startsWith(".env."))return!0;let o=e.replace(/\\/g,"/");if(o.startsWith("supabase/")&&t.endsWith(".sql")||o.startsWith(".github/workflows/")&&(t.endsWith(".yml")||t.endsWith(".yaml")))return!0;let a=Me.extname(t);return Ao.has(a)}async function ds(e){let t=await To(e);return t.subarray(0,Math.min(t.length,8192)).includes(0)}async function zo(e,t,o,a,n){let i;try{i=await is(t,{withFileTypes:!0})}catch{return}for(let r of i){let s=Me.join(t,r.name),c=Me.relative(e,s).replace(/\\/g,"/");if(r.isDirectory()){if(ss.has(r.name))continue;await zo(e,s,o,a,n);continue}if(!r.isFile()||!us(c,r.name))continue;let l=ls(c,r.name);if(!l)continue;let u;try{u=await rs(s)}catch{continue}if(u.size>Ie){a.push(c);continue}if(await ds(s)){n.push(c);continue}o.push({relativePath:c,absolutePath:s,sizeBytes:u.size,kind:l})}}async function Lo(e){let t=[],o=[],a=[],n=[];return await zo(e,e,t,o,a),t.sort((i,r)=>i.relativePath.localeCompare(r.relativePath)),{files:t,skippedLargeFiles:o,skippedBinaryFiles:a,warnings:n}}async function M(e){return To(e.absolutePath,"utf8")}var ms=[{prefix:"NEXT_PUBLIC_",ecosystemLabel:"Next.js"},{prefix:"NUXT_PUBLIC_",ecosystemLabel:"Nuxt"},{prefix:"EXPO_PUBLIC_",ecosystemLabel:"Expo"},{prefix:"REACT_APP_",ecosystemLabel:"Create React App"},{prefix:"VITE_",ecosystemLabel:"Vite"},{prefix:"GATSBY_",ecosystemLabel:"Gatsby"},{prefix:"PUBLIC_",ecosystemLabel:"SvelteKit / generic public"}],De=new Set(["NEXT_PUBLIC_SUPABASE_ANON_KEY","VITE_FIREBASE_API_KEY","NEXT_PUBLIC_FIREBASE_API_KEY"]),Uo="Client-exposed environment variable contains private-looking value",Io="Deployment workflow exposes private-looking value to frontend",Mo="(?:sk_(?:live|test)_|sk-(?:ant-|proj-)?|AIza|(?:AKIA|ASIA)|ghp_|github_pat_|gho_|ghu_|ghs_|ghr_)";function Do(e){for(let{prefix:t,ecosystemLabel:o}of ms)if(e.startsWith(t))return{envName:e,prefix:t,ecosystemLabel:o};return null}function ps(e){return new RegExp(Mo).test(e)}function Oo(e){let t=e.trim();if(!t||t.startsWith("#"))return null;let o=t.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!o?.[1])return null;let a=o[1],n=Do(a);if(!n||De.has(a))return null;let i=(o[2]??"").trim();if((i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),!ps(i))return null;let r=t.indexOf(a);return{...n,value:i,matchIndex:r>=0?r:0}}function No(e){let o=new RegExp(`\\b([A-Z][A-Z0-9_]*)\\s*[:=]\\s*['"]?(${Mo}[^\\s#'"]*)`).exec(e);if(!o?.[1]||!o[2])return null;let a=o[1],n=Do(a);return!n||De.has(a)?null:{...n,value:o[2],matchIndex:o.index}}function $o(e,t){return`${e.envName} (${e.ecosystemLabel}, ${e.prefix}) in ${t} uses a value that resembles a private API key.`}function Bo(e,t){return`${e}* variables (${t}) are exposed to the browser; values resembling private keys should be reviewed.`}function qo(e,t){return`${t} assigns a private-looking key to ${e.envName} (${e.ecosystemLabel}, ${e.prefix}).`}function Fo(e,t){return`CI workflows must not publish private key patterns to ${e}* variables (${t}) that reach browser bundles.`}function Oe(e){return{envName:e.envName,prefix:e.prefix,ecosystemLabel:e.ecosystemLabel}}var gs=[/your[_-]?key[_-]?here/i,/changeme/i,/replace[_-]?me/i,/\bexample\b/i,/\btest\b/i,/\bdummy\b/i,/\bplaceholder\b/i,/\bxxx+\b/i,/<secret>/i],fs=[{id:"secrets.stripe-secret-key",regex:/\bsk_(live|test)_[A-Za-z0-9]{8,}\b/g,title:"Stripe secret key",tag:"stripe"},{id:"secrets.openai-api-key",regex:/\bsk-proj-[A-Za-z0-9_-]{8,}\b/g,title:"OpenAI API key",tag:"openai"},{id:"secrets.anthropic-api-key",regex:/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,title:"Anthropic API key",tag:"anthropic"},{id:"secrets.openai-api-key",regex:/\bsk-(?!ant-|proj-)[A-Za-z0-9_-]{12,}\b/g,title:"OpenAI API key",tag:"openai"},{id:"secrets.google-api-key",regex:/\bAIza[A-Za-z0-9_-]{20,}\b/g,title:"Google API key",tag:"google"},{id:"secrets.aws-access-key",regex:/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,title:"AWS access key",tag:"aws"},{id:"secrets.github-token",regex:/\b(ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|gho_[A-Za-z0-9_]{20,}|ghu_[A-Za-z0-9_]{20,}|ghs_[A-Za-z0-9_]{20,}|ghr_[A-Za-z0-9_]{20,})\b/g,title:"GitHub token",tag:"github"},{id:"secrets.private-key-block",regex:/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/g,title:"Private key block",tag:"private-key"}],Ho=/\b(?:JWT_SECRET|SESSION_SECRET|AUTH_SECRET|NEXTAUTH_SECRET|SECRET_KEY)\s*=\s*['"]([^'"]{12,})['"]/gi;function Z(e){let t=e.trim();if(t.length<=4)return"***";let o=[{prefix:"sk_live_"},{prefix:"sk_test_"},{prefix:"sk-proj-"},{prefix:"sk-ant-"},{prefix:"sk-"},{prefix:"github_pat_"},{prefix:"ghp_"},{prefix:"gho_"},{prefix:"ghu_"},{prefix:"ghs_"},{prefix:"ghr_"},{prefix:"AKIA"},{prefix:"ASIA"},{prefix:"AIza"}];for(let{prefix:i}of o)if(t.startsWith(i)&&t.length>i.length+4)return`${i}***${t.slice(-4)}`;if(t.includes("PRIVATE KEY"))return"-----BEGIN *** PRIVATE KEY-----";let a=t.slice(-4);return`${t.slice(0,Math.min(6,t.length-4))}***${a}`}function hs(e){let t=e.trim();if(!t||t.startsWith("#"))return null;let o=t.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!o?.[1])return null;let a=(o[2]??"").trim();return(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),{name:o[1],value:a}}function ys(e,t){let o=hs(e);return!o||!De.has(o.name)?!1:o.value.includes(t)}function bs(e){return gs.some(t=>t.test(e))}function Ct(e){return e.length<8||bs(e)||/^(true|false|null|undefined|\d+)$/i.test(e)?!1:/[A-Za-z0-9]/.test(e)}function _t(e,t,o,a){return{file:e,line:t,column:o,excerpt:Z(a),redactionApplied:!0}}function ks(e,t,o,a){let n=[],i=new Set;for(let c of fs){c.regex.lastIndex=0;let l;for(;(l=c.regex.exec(o))!==null;){let u=l[0];if(ys(o,u)||!Ct(u))continue;let d=`${c.id}:${t}:${l.index}`;i.has(d)||(i.add(d),n.push({id:c.id,category:"secrets",riskKind:"secret_exposure",status:"observed",confidence:"high",matchType:"regex",title:c.title,detail:`Matched ${c.tag} secret pattern in ${e}.`,reason:a?"Sample env file contains a value that looks like a real secret rather than a placeholder.":"Source file contains a value matching a known secret credential pattern.",sources:[_t(e,t,l.index+1,u)],tags:[c.tag,"secret-detector"]}))}}Ho.lastIndex=0;let r;for(;(r=Ho.exec(o))!==null;){let c=r[1]??"";if(!Ct(c))continue;let l=`secrets.hardcoded-auth-secret:${t}:${r.index}`;i.has(l)||(i.add(l),n.push({id:"secrets.hardcoded-auth-secret",category:"secrets",riskKind:"secret_exposure",status:"observed",confidence:"medium",matchType:"heuristic",title:"Hardcoded auth secret",detail:`Hardcoded session/JWT secret in ${e}.`,reason:"Environment or config assignment uses a literal value that looks like a real auth secret.",sources:[_t(e,t,r.index+1,c)],tags:["auth-secret","secret-detector"]}))}let s=Oo(o);if(s&&Ct(s.value)){let c=`secrets.next-public-private-pattern:${s.envName}:${t}`;i.has(c)||(i.add(c),n.push({id:"secrets.next-public-private-pattern",category:"frontend_exposure",riskKind:"secret_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:Uo,detail:$o(s,e),reason:Bo(s.prefix,s.ecosystemLabel),sources:[_t(e,t,s.matchIndex+1,s.value)],tags:["client-exposed-env",s.prefix.replace(/_$/,"").toLowerCase(),"secret-detector"],metadata:Oe(s)}))}return n}async function Vo(e){let t=[],o=new Set;for(let a of e){let i=(await M(a)).split(/\r?\n/),r=a.relativePath===".env.example"||a.relativePath===".env.sample"||a.relativePath.endsWith("/.env.example")||a.relativePath.endsWith("/.env.sample");for(let s=0;s<i.length;s+=1){let c=i[s]??"",l=ks(a.relativePath,s+1,c,r);for(let u of l){let d=`${u.id}:${u.sources.map(m=>`${m.file}:${m.line}`).join(",")}`;o.has(d)||(o.add(d),t.push(u))}}}return t}var Rt=/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;function de(e,t,o,a){return{file:e,line:t,column:a,excerpt:Z(o),redactionApplied:!0}}function vs(e,t){let o=[],a=t.split(/\r?\n/),n=new Set;for(let i=0;i<a.length;i+=1){let r=a[i]??"",s=i+1;if(/\bNEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-next-public:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-next-public",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"observed",confidence:"high",matchType:"exact",title:"Supabase service role exposed via NEXT_PUBLIC",detail:`${e} references NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY.`,reason:"Service role keys must not use NEXT_PUBLIC_* names because they are bundled for the browser.",sources:[de(e,s,r.trim())],tags:["supabase","service-role","next-public"]}))}if(/\bSUPABASE_SERVICE_ROLE_KEY\b/.test(r)&&!/\bNEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-reference:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-reference",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"medium",matchType:"exact",title:"Supabase service role key reference",detail:`${e} references SUPABASE_SERVICE_ROLE_KEY.`,reason:"Privileged Supabase service role material was referenced; verify it is server-only and not committed.",sources:[de(e,s,r.trim())],tags:["supabase","service-role"]}))}if(/\bservice_role\b/i.test(r)&&!/\bSUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-reference:literal:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-reference",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Supabase service_role reference",detail:`${e} mentions service_role.`,reason:"Literal service_role references may indicate privileged Supabase client configuration.",sources:[de(e,s,r.trim())],tags:["supabase","service-role"]}))}if(/\bNEXT_PUBLIC_[A-Z0-9_]*SUPABASE[A-Z0-9_]*\b/.test(r)&&Rt.test(r)&&!/\bNEXT_PUBLIC_SUPABASE_ANON_KEY\b/.test(r)){Rt.lastIndex=0;let c=`baas.supabase-jwt-frontend-env:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-next-public",category:"frontend_exposure",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"JWT-like Supabase value in frontend env",detail:`${e} assigns a JWT-like value to a NEXT_PUBLIC Supabase variable.`,reason:"JWT-shaped values in NEXT_PUBLIC Supabase env names may expose privileged credentials to the browser.",sources:[de(e,s,r.trim())],tags:["supabase","jwt","next-public"]})),Rt.lastIndex=0}if(/createClient\s*\([^)]*(?:service_role|SUPABASE_SERVICE_ROLE_KEY)/i.test(r)){let c=`baas.supabase-service-role-client-exposure:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-client-exposure",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"observed",confidence:"high",matchType:"heuristic",title:"Supabase client configured with service role",detail:`${e} passes service role material into createClient.`,reason:"createClient should use anon keys in browser-facing code; service role bypasses RLS.",sources:[de(e,s,r.trim())],tags:["supabase","createClient","service-role"]}))}}return o}async function Ko(e){let t=e.filter(n=>n.kind==="source"||n.kind==="env"||n.kind==="config"||n.kind==="sql"||n.kind==="devops"),o=[],a=new Set;for(let n of t){let i=await M(n);for(let r of vs(n.relativePath,i)){let s=`${r.id}:${r.sources.map(c=>`${c.file}:${c.line}`).join(",")}`;a.has(s)||(a.add(s),o.push(r))}}return o}function me(e){let t=e.replace(/\\/g,"/"),o=t.split("/").filter(Boolean);return o[o.length-1]??t}function ws(e,t){let o=t.startsWith("/")?t:`/${t}`,a=[];for(let n of e){let i=n.replace(/\\/g,"/");(i===t.replace(/^\//,"")||i.endsWith(o))&&a.push(n)}return a.sort((n,i)=>n.localeCompare(i))}function Q(e,t){return ws(e,t)[0]}function Ne(e,t){let o=[];for(let a of e)me(a)===t&&o.push(a);return o.sort((a,n)=>a.localeCompare(n))}function ee(e,t){return Ne(e,t)[0]}var xs=[".env",".env.local",".env.production",".env.development.local"],Ss=["package-lock.json","pnpm-lock.yaml","yarn.lock","bun.lockb"],Es=[/\bsk_(live|test)_[A-Za-z0-9]{8,}\b/,/\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{12,}\b/,/\bAIza[A-Za-z0-9_-]{20,}\b/,/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/,/\b(ghp_|github_pat_|gho_|ghu_|ghs_|ghr_)[A-Za-z0-9_]{20,}\b/];function Cs(e){return new Map(e.map(t=>[t.relativePath,t]))}function _s(e){return e?e.split(/\r?\n/).map(a=>a.trim()).filter(a=>a&&!a.startsWith("#")).some(a=>a===".env"||a===".env*"||a===".env.*"):!1}function Rs(e){return/^\s*USER\s+(?!root\b)/im.test(e)}async function pe(e,t){let o=e.get(t)??[...e.values()].find(a=>me(a.relativePath)===me(t));return o?M(o):null}async function Wo(e,t){let o=Cs(t),a=[],n=Q(o.keys(),".gitignore")??ee(o.keys(),".gitignore"),i=Q(o.keys(),".dockerignore")??ee(o.keys(),".dockerignore"),r=Q(o.keys(),"Dockerfile")??ee(o.keys(),"Dockerfile"),s=Q(o.keys(),"package.json")??ee(o.keys(),"package.json"),c=n?await pe(o,n):null,l=i?await pe(o,i):null,u=r?await pe(o,r):null,d=s?await pe(o,s):null,m=xs.filter(f=>o.has(f)||[...o.keys()].some(w=>me(w)===f));m.length>0&&!_s(c)&&a.push({id:"devops.env-not-gitignored",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"high",matchType:"absence",title:"Env files may not be gitignored",detail:`Found ${m.join(", ")} but .gitignore does not ignore .env or .env*.`,reason:"Environment files with secrets often exist in the repo; missing .env gitignore patterns increase accidental commit risk.",sources:m.map(f=>({file:Q(o.keys(),f)??ee(o.keys(),f)??f,line:1})),tags:["gitignore","env"],metadata:{envFiles:m}});let g=!!u,y=!!l;g&&!y&&a.push({id:"devops.dockerignore-missing",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"medium",matchType:"absence",title:".dockerignore is missing",detail:"Dockerfile exists but no .dockerignore was found.",reason:"Without .dockerignore, Docker builds may copy local artifacts (including .env or .git) into images.",sources:[{file:r??"Dockerfile",line:1}],tags:["docker","dockerignore"]}),g&&u&&/COPY\s+\.\s+\./i.test(u)&&!y&&a.push({id:"devops.docker-copy-all-without-dockerignore",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"high",matchType:"heuristic",title:"Dockerfile copies entire build context",detail:"Dockerfile uses COPY . . without a .dockerignore file.",reason:"COPY . . can bundle secrets and VCS metadata into images when .dockerignore is absent.",sources:[{file:r??"Dockerfile",line:u.split(/\r?\n/).findIndex(f=>/COPY\s+\.\s+\./i.test(f))+1||1}],tags:["docker","copy"]}),g&&u&&!Rs(u)&&a.push({id:"devops.docker-non-root-user-missing",category:"devops_deployment",riskKind:"deployment_leakage",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Dockerfile may run as root",detail:"Dockerfile does not declare a non-root USER instruction.",reason:"Containers that run as root increase blast radius if the workload is compromised.",sources:[{file:r??"Dockerfile",line:1}],tags:["docker","user"]});let v=t.filter(f=>f.kind==="devops"&&f.relativePath.startsWith(".github/workflows/"));for(let f of v){let P=(await M(f)).split(/\r?\n/);for(let j=0;j<P.length;j+=1){let T=P[j]??"",_=j+1;Es.some($=>$.test(T))&&a.push({id:"devops.github-actions-hardcoded-secret",category:"devops_deployment",riskKind:"secret_exposure",status:"observed",confidence:"high",matchType:"regex",title:"Hardcoded secret in GitHub Actions workflow",detail:`${f.relativePath} contains a hardcoded secret-like value.`,reason:"CI workflow files should reference GitHub secrets instead of embedding credential material.",sources:[{file:f.relativePath,line:_,excerpt:Z(T.trim()),redactionApplied:!0}],tags:["github-actions","ci"]}),/\becho\b.*\b(?:SECRET|TOKEN|API_KEY|PASSWORD)\b/i.test(T)&&a.push({id:"devops.github-actions-echo-secret-env",category:"devops_deployment",riskKind:"deployment_leakage",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"Workflow echoes secret-like env names",detail:`${f.relativePath} echoes environment variables that look like secrets.`,reason:"Echoing secret env names in CI logs can leak values when shell tracing or expansion is enabled.",sources:[{file:f.relativePath,line:_,excerpt:T.trim().slice(0,120)}],tags:["github-actions","echo"]});let C=No(T);C&&a.push({id:"devops.github-actions-next-public-private",category:"frontend_exposure",riskKind:"secret_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:Io,detail:qo(C,f.relativePath),reason:Fo(C.prefix,C.ecosystemLabel),sources:[{file:f.relativePath,line:_,excerpt:Z(T.trim()),redactionApplied:!0}],tags:["github-actions","client-exposed-env",C.prefix.replace(/_$/,"").toLowerCase()],metadata:Oe(C)})}}let b=Ss.flatMap(f=>Ne(o.keys(),f));b.length>1&&a.push({id:"devops.multiple-lockfiles",category:"dependencies",riskKind:"supply_chain_hygiene",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"Multiple package lockfiles detected",detail:`Found lockfiles: ${b.join(", ")}.`,reason:"Multiple lockfiles can cause inconsistent dependency resolution across environments.",sources:b.map(f=>({file:f,line:1})),tags:["lockfile","dependencies"],metadata:{lockfiles:[...b]}}),d&&b.length===0&&a.push({id:"devops.lockfile-missing",category:"dependencies",riskKind:"supply_chain_hygiene",status:"observed",confidence:"high",matchType:"absence",title:"package.json without lockfile",detail:"package.json exists but no package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lockb was found.",reason:"Missing lockfiles make dependency installs non-deterministic and harder to audit.",sources:[{file:s??"package.json",line:1}],tags:["lockfile","dependencies"]});let x=["next.config.js","next.config.mjs","next.config.ts"].flatMap(f=>Ne(o.keys(),f));for(let f of x){let w=await pe(o,f);if(w&&/productionBrowserSourceMaps\s*:\s*true/.test(w)){let P=w.split(/\r?\n/).findIndex(j=>/productionBrowserSourceMaps\s*:\s*true/.test(j))+1;a.push({id:"devops.production-browser-source-maps",category:"frontend_exposure",riskKind:"deployment_leakage",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Production browser source maps enabled",detail:`${f} sets productionBrowserSourceMaps: true.`,reason:"Source maps can be legitimate for debugging but should be reviewed for production exposure of client-side code.",sources:[{file:f,line:P||1}],tags:["nextjs","source-maps"]})}}return a}function Ps(e){let t=new Set,o=[];for(let a of e){let n=[a.id,a.category,...a.sources.map(i=>`${i.file}:${i.line}:${i.column??0}`)].join("|");t.has(n)||(t.add(n),o.push(a))}return o}function Ts(e){return[...e].sort((t,o)=>{let a=t.category.localeCompare(o.category);if(a!==0)return a;let n=t.id.localeCompare(o.id);if(n!==0)return n;let i=t.sources[0]?.file??"",r=o.sources[0]?.file??"",s=i.localeCompare(r);return s!==0?s:(t.sources[0]?.line??0)-(o.sources[0]?.line??0)})}function As(e){let t=0,o=0,a=0;for(let n of e)n.status==="observed"&&(t+=1),n.status==="needs_review"&&(o+=1),n.status==="not_applicable"&&(a+=1);return{signalsObserved:t,needsReview:o,notApplicable:a}}async function Go(e){let{root:t,scanId:o,generatedAt:a=new Date().toISOString()}=e,n=await Lo(t),i=await Vo(n.files),r=await Ko(n.files),s=await Wo(t,n.files),c=[{id:"secrets",status:"completed",filesScanned:n.files.length,signals:i.length,warnings:[]},{id:"baas",status:"completed",filesScanned:n.files.length,signals:r.length,warnings:[]},{id:"devops",status:"completed",filesScanned:n.files.length,signals:s.length,warnings:n.warnings}],l=Ts(Ps([...i,...r,...s])),u=As(l),d=await Promise.all(n.files.map(async m=>({path:m.relativePath,content:await M(m)})));return{version:_e,scanId:o,generatedAt:a,root:t,summary:{filesScanned:n.files.length,filesSkipped:n.skippedLargeFiles.length+n.skippedBinaryFiles.length,signalsObserved:u.signalsObserved,needsReview:u.needsReview,notApplicable:u.notApplicable},detectors:c,signals:l,repoSnapshot:{paths:n.files.map(m=>m.relativePath),files:d},diagnostics:{maxFileBytes:Ie,skippedLargeFiles:n.skippedLargeFiles,skippedBinaryFiles:n.skippedBinaryFiles,warnings:n.warnings}}}import{mkdir as js,writeFile as zs}from"node:fs/promises";import Yo from"node:path";async function Xo(e,t){let o=Yo.join(e,".vibemole","security");await js(o,{recursive:!0});let a=Yo.join(o,"evidence.json");return await zs(a,`${JSON.stringify(t,null,2)}
|
|
2566
|
-
`,"utf8"),a}var Is=Jo.dirname(Us(import.meta.url)),Ms=Jo.resolve(Is,"../../..");function Ds(e){return!e&&!!(D.stdin.isTTY&&D.stdout.isTTY)}async function Os(){let e;try{e=Wt(D.argv.slice(2))}catch(s){return console.error(s instanceof Error?s.message:String(s)),console.error(Ye()),1}if(e.command==="help")return console.log(Ye()),0;if(e.command==="login"){let s=`${D.env.USER||"user"}@${Ls.hostname()}`;return await Ee(e.webUrl,s),0}if(e.command==="logout")return await gn(),console.log("Logged out of VibeMole CLI."),0;if(e.command==="whoami"){let s=await H(),c=xe(s,e);if("error"in c)return console.error(c.error),1;if(se(s))return console.error("CLI session expired. Run: vibemole login"),1;let l=await pn(c.webUrl,s.token);return console.log(`userId: ${l.userId}`),console.log(`tokenPrefix: ${l.tokenPrefix}`),console.log(`webUrl: ${s.webUrl}`),0}let t=D.cwd(),o=Ds(e.json),a=new Pe(D.stderr,!e.json),n=e.url,i=null,r=0;try{let s;if(await Et(a,e.verbose,{start:xo,success:So},async()=>{s=await Sn(t)}),!n&&!e.noRuntime){let v=await En({root:t,onOutput:e.verbose?b=>D.stderr.write(b):void 0});n=v.targetUrl,i=v.process,n||(n=await $n({interactive:o,ask:ie,log:b=>console.error(b)}))}let c=[],l=[],u=!1;if(!e.noRuntime&&n){let v=await Ue();if(!v.available&&o&&await ae("No browser runtime was found. Install Playwright Chromium now to run runtime checks?",!0)&&(await Yt({repoRoot:Ms,cwd:t}),v=await Ue()),v.available){let b=await an({root:t,targetUrl:n,noLogin:e.noLogin,loginRequired:e.loginRequired,loginMethod:e.loginMethod,loginRefresh:e.loginRefresh,interactive:o});u=b.authenticated,!b.authenticated&&e.loginRequired&&e.verbose&&console.error(`Authenticated scan not available: ${b.reason}`);let x=await Po({targetUrl:n,projectEvidence:s,authenticated:u,storageState:b.storageState,browserRuntime:v,progress:a,routeCap:e.runtimeRouteCap,pagesPerLevel:e.runtimePagesPerLevel,acceptLanguage:ve(e.locale),verbose:e.verbose});c=x.profiles,l=x.warnings}else console.error("Browser runtime unavailable; evidence package will include project and policy-route evidence only.")}let d,m;await Et(a,e.verbose,{start:Eo,success:Co},async()=>{d=await On({root:t,targetUrl:n,reportLocale:e.locale,collectorRouteCap:e.runtimeRouteCap,projectEvidence:s,runtimeProfiles:c,collectorWarnings:l}),m=await Dn(t,d)});let g=null;e.security&&(g=await Go({root:t,scanId:d.scanId}),e.noUpload&&await Xo(t,g));let y=null;if(e.noUpload||(y=await Ro({args:e,evidencePackage:d,securityEvidence:g,progress:a,...e.security?{forceUpload:!0}:{}}),r=y.exitCode),e.json){let v=Te(d,g);e.security&&!e.noUpload&&y?console.log(JSON.stringify({...v,scanId:y.scanId??d.scanId,...y.reportUrl?{reportUrl:y.reportUrl}:{}},null,2)):console.log(JSON.stringify(v,null,2))}else r===0
|
|
2566
|
+
})()`}async function xt(){let e=io(import.meta.url),t=Y.dirname(jr(import.meta.url)),o=Y.resolve(t,"../../..");for(let a of[process.cwd(),Y.join(o,"apps","cli"),Y.join(o,"apps","worker"),Y.join(o,"apps","web")])try{return io(Y.join(a,"package.json"))("playwright")}catch{}try{return e("playwright")}catch{return null}}async function zr(e,t,o){return await(await e.chromium.launch(o)).close(),{available:!0,name:t,launchOptions:o}}async function Ue(e={}){let t=e.playwright??await xt(),o=[];if(!t)return{available:!1,installHint:"Run: npx playwright install chromium",attempts:[{name:"playwright-chromium",error:"playwright package is not available"}]};let a=[{name:"chrome",launchOptions:{channel:"chrome",headless:!0}},{name:"msedge",launchOptions:{channel:"msedge",headless:!0}},{name:"playwright-chromium",launchOptions:{headless:!0}}];for(let n of a)try{return await zr(t,n.name,n.launchOptions)}catch(i){o.push({name:n.name,error:i instanceof Error?i.message:String(i)})}return{available:!1,installHint:"Run: npx playwright install chromium",attempts:o}}function wt(e){try{return new URL(e).hostname}catch{return null}}function X(e){return[...new Set(e.filter(t=>!!t))].sort()}function Le(e){return/(posthog|google-analytics|googletagmanager|segment|mixpanel|amplitude|clarity|facebook|doubleclick)/i.test(e)}var Lr=10;function po(e){try{return new URL(e).hostname}catch{return null}}function Ur(e){return e==="local_storage"?"localStorage":e==="session_storage"?"sessionStorage":"indexedDB"}function Ir(e){if(e.key)return e.key;if(e.storageType!=="indexeddb")return null;let t=[e.databaseName,e.objectStoreName,e.collectionStatus].filter(Boolean);return t.length>0?t.join(":"):null}function Mr(e){return Fr(e.flatMap(t=>{let o=Ir(t);return o?[{type:Ur(t.storageType),key:o,value:t.value}]:[]}))}var Dr=200,ro=160,Or=220,Nr=100,$r=100,Br=20;function qr(e){let t=new Set,o=[];for(let a of e){let n=a.text.length>ro?a.text.slice(0,ro):a.text,i=`${a.href}\0${n}`;if(!t.has(i)&&(t.add(i),o.push({...a,text:n}),o.length>=Dr))break}return o}function Fr(e){let t={localStorage:Nr,sessionStorage:$r,indexedDB:Br},o={localStorage:0,sessionStorage:0,indexedDB:0},a=[];for(let n of e)if(!(o[n.type]>=t[n.type])&&(o[n.type]+=1,a.push(n),a.length>=Or))break;return a}function Hr(e){return e.cookies.some(t=>/^(__ph_|ph_|posthog)/i.test(t.name))||e.storage.some(t=>/^(__ph_|ph_|posthog)/i.test(t.key))||e.knownGlobals.some(t=>/posthog/i.test(t))}function St(e){try{let t=new URL(e);return/posthog/i.test(t.hostname)||/\/array\/phc_/i.test(t.pathname)||/\/flags\/?$/i.test(t.pathname)||/\/static\/(?:exception-autocapture|surveys|dead-clicks-autocapture|web-vitals)\.js$/i.test(t.pathname)}catch{return!1}}function so(e){let t=new Set(e.trackerDomains.map(a=>a.toLowerCase()));return X(e.urls).filter(a=>{let n=po(a)?.toLowerCase();return n?t.has(n)||Le(n)||St(a):!1}).slice(0,e.limit??25)}function Vr(e){let t=new Set(e.domains.filter(Le)),o=Hr(e);for(let a of e.urls){let n=po(a);n&&(Le(n)||St(a)||o&&St(a))&&t.add(n)}if(e.knownGlobals.some(a=>/^(dataLayer|google_tag_manager|gtag|ga|fbq|ttq|hj|clarity|ym|analytics|amplitude)$/i.test(a)))for(let a of e.domains.filter(Le))t.add(a);return[...t].sort()}var co=/(?:^|\/)(?:privacy|cookie|cookies|legal|terms|gdpr|impressum|data-protection|datenschutz)(?:\/|$)/i;function Kr(e){return!e||e==="/"?"/":e.startsWith("/")?e:`/${e}`}function go(e){let t=new Set(["/"]);for(let i of e)t.add(Kr(i));let o=[...t],a=o.filter(i=>i!=="/"&&co.test(i)).sort(),n=o.filter(i=>i!=="/"&&!co.test(i)).sort();return["/",...a,...n]}function Wr(e,t,o){let a=new Set;return t.map(n=>{try{return new URL(n,e).toString()}catch{return null}}).filter(n=>!!n).filter(n=>a.has(n)?!1:(a.add(n),!0)).slice(0,o)}function fo(e){let t=e.routeCap??8,o=e.routes?.length?e.routes:["/"],a=e.consentModes??["none","accepted","declined"];return Wr(e.targetUrl,o,t).flatMap(i=>a.map(r=>({routeUrl:i,consentMode:r,authenticated:e.authenticated})))}function ho(e){return fo({...e,consentModes:["none"]})}function yo(e){return fo({targetUrl:e.targetUrl,routes:[e.routeUrl],authenticated:e.authenticated,routeCap:1,consentModes:["accepted","declined"]})}async function J(e,t){return e.evaluate(t)}async function ue(e,t){await new Promise(o=>setTimeout(o,t))}async function Gr(e,t){let o=e.waitForLoadState?.("networkidle",{timeout:t});o&&await o.catch(()=>{})}var Yr=2e3,Xr=100;function lo(e){return e.metaDescription?e.faviconFromExplicitLink!==!0:!0}async function Jr(e){let t=await J(e,vt());if(!lo(t))return t;let o=Date.now()+Yr;for(;Date.now()<o&&(await ue(e,Xr),t=await J(e,vt()),!!lo(t)););return t}async function uo(e){return J(e,bt({checkBanner:!0,acceptedModeKeywords:Ae,declinedModeKeywords:je,settingsModeKeywords:gt}))}function mo(e){return e.consentBannerFound==="yes"&&q(e)}async function Zr(e){let t=await uo(e);if(!mo(t)){for(let o of ft)if(await ue(e,o),t=await uo(e),mo(t))break}return t}async function bo(e){let t=await xt();if(!t)return ze("playwright package is not available");let o=e.browserRuntime??await Ue({playwright:t});if(!o.available)return ze("browser runtime is not available");let a=await t.chromium.launch(o.launchOptions);try{let n=await xt();if(await a.close(),!n)return ze("playwright package is not available");let i=await n.chromium.launch(o.launchOptions);try{let r=await i.newContext({storageState:e.storageState,...e.acceptLanguage?{extraHTTPHeaders:{"Accept-Language":e.acceptLanguage}}:{}}),s=await r.newPage(),c=[],l={};s.on("request",h=>{c.push(h.url())});let u=await s.goto(e.targetUrl,{waitUntil:"load",timeout:e.timeoutMs??15e3});await Gr(s,yt);let d=e.consentMode==="none"?1500:1200;await ue(s,d);let m=await Zr(s),g=to(m),y=m.consentBannerSelectorForCapture,v={attempted:!1,succeeded:!0,action:"none"};if(e.consentMode&&e.consentMode!=="none"){let h=await J(s,kt({mode:e.consentMode,acceptedModeKeywords:Ae,declinedModeKeywords:je,bannerRootSelector:y}));v=no(e.consentMode,h,m),(h.consentOutcome==="clicked_accept"||h.consentOutcome==="clicked_decline")&&(await ue(s,700),await ue(s,ht),await s.waitForLoadState?.("networkidle",{timeout:5e3}).catch(()=>{}))}let b=u?.headers?.()??{};for(let h of["content-security-policy","strict-transport-security","x-frame-options","x-content-type-options","referrer-policy"])l[h]=b[h]??null;let x=await s.title().catch(()=>null),f=x&&x.trim()?x.trim():null,w=await J(s,oo(Lr)),P=Mr(w),j=qr(await s.evaluate(()=>Array.from(document.querySelectorAll("a[href]")).map((h,z)=>{let R=h;return{href:R.href||R.getAttribute("href")||"",text:(R.textContent||R.getAttribute("aria-label")||"").replace(/\s+/g," ").trim(),selector:`a[href]:nth-of-type(${z+1})`}}))),T=await r.cookies(),_=await J(s,ao()),C=await Jr(s),$=X([...c,..._.resourceUrls,..._.loadedScriptUrls]),U=X(c.map(wt)),ge=X([...c.filter(h=>/\.(js|mjs)(\?|$)/.test(h)),..._.loadedScriptUrls].map(wt)),te=X($.map(wt)),A=Vr({domains:te,urls:$,cookies:T,storage:P,knownGlobals:_.knownGlobals}),I=await Qr(s,!!e.includeAccessibility);return await r.close(),{characterization:m,evidence:{navigationSucceeded:!0,finalUrl:s.url(),status:u?.status?.()??null,title:f,securityHeaders:l,cookies:T.map(h=>{let z=h.expires,R=Number.isFinite(z)&&(z??0)>0;return{name:h.name,value:h.value,domain:h.domain,path:h.path,expiry:R?new Date(z*1e3).toISOString():null,isSession:!R,secure:h.secure,httpOnly:h.httpOnly,sameSite:h.sameSite}}),storage:P,requestDomains:U,scriptDomains:ge,resourceDomains:te,trackerDomains:A,loadedScriptUrls:so({urls:_.loadedScriptUrls,trackerDomains:A}),resourceUrls:so({urls:_.resourceUrls,trackerDomains:A}),pageSiteSignals:{faviconUrl:C.faviconUrl??null,faviconFromExplicitLink:C.faviconFromExplicitLink===!0,metaDescription:C.metaDescription??null,canonicalUrl:C.canonicalUrl??null,htmlLang:C.htmlLang??null,policyLinkUrls:X((C.policyIndicators??[]).map(h=>h.url).filter(h=>typeof h=="string"&&h.length>0))},knownGlobals:_.knownGlobals,renderedLinks:j,consent:g,accessibility:I},consentInteraction:v}}finally{await i.close()}}catch(n){return ze(n instanceof Error?n.message:String(n))}}async function Qr(e,t){if(!t)return{scanned:!1,violations:[],skippedReason:"accessibility scan disabled"};try{let a=await new Function("specifier","return import(specifier)")("@axe-core/playwright"),n=a.default??a.AxeBuilder;return{scanned:!0,violations:(await new n({page:e}).analyze()).violations.map(r=>({id:String(r.id),impact:r.impact?String(r.impact):null,description:String(r.description),nodes:Array.isArray(r.nodes)?r.nodes.length:0}))}}catch(o){return{scanned:!1,violations:[],skippedReason:o instanceof Error?o.message:"axe scan unavailable"}}}function es(e){return{navigationSucceeded:!1,finalUrl:null,status:null,title:null,securityHeaders:{},cookies:[],storage:[],requestDomains:[],scriptDomains:[],resourceDomains:[],trackerDomains:[],consent:{bannerFound:!1,acceptPresent:!1,rejectPresent:!1,settingsPresent:!1,detectionConfidence:"low",detectionBasis:e},accessibility:{scanned:!1,violations:[],skippedReason:e},skippedReason:e}}function ze(e){return{characterization:{consentBannerFound:"no",consentDetectionConfidence:"low",consentBannerDetectionBasis:e,consentCaptureQualityClass:"unusable",consentButtons:{acceptPresent:!1,declinePresent:!1,settingsPresent:!1},consentBannerSelectorForCapture:null,consentUx:null},evidence:es(e),consentInteraction:{attempted:!1,succeeded:!1,action:"none",error:e}}}var xo="Scanning project evidence...",So="Project evidence scanned.",Eo="Building redacted evidence package...",Co="Redacted evidence package built.",_o=10,ts=2;async function Et(e,t,o,a){t&&e.start(o.start),await a(),t&&e.success(o.success)}async function ns(e,t,o,a=null){let n={"content-type":"application/json"};o&&(n.authorization=`Bearer ${o}`);let i=Te(t,a),r=await fetch(e,{method:"POST",headers:n,body:JSON.stringify(i)});if(!r.ok){let s=await r.text().catch(()=>"");throw new Error(`Upload failed with ${r.status}${s?`: ${s.slice(0,240)}`:""}`)}return await r.json().catch(()=>({}))}async function Ro(e){if(!(e.forceUpload??Bn(e.args)))return{exitCode:0};let o=e.sink??{log:r=>console.log(r),error:r=>console.error(r)},a=e.upload??ns,n=e.poll??Jn,i=await qn(e.args,e.prepareDeps);if(!i.ok)return o.error(i.error),{exitCode:1};e.progress.start("Uploading redacted evidence package...");try{let r=await a(i.uploadUrl,e.evidencePackage,i.session.token,e.securityEvidence??null);e.progress.success("Upload succeeded.");let s=r.scanId??e.evidencePackage.scanId,c=i.session.webUrl;if(r.analysisStatus==="stored_only")return o.log(r.message??"Evidence stored. Report analysis is not generated yet."),{exitCode:0,scanId:s,reportUrl:r.reportUrl?G(c,s,r.reportUrl):void 0};if(r.analysisStatus==="pending"&&r.statusUrl){e.progress.start("Waiting for security grading...");let l=await n({statusUrl:r.statusUrl,bearerToken:i.session.token,webUrl:c,scanId:s},e.pollDeps);return e.progress.finishLine(),l.outcome==="ready"?{exitCode:0,scanId:s,reportUrl:G(c,s,l.reportUrl)}:l.outcome==="failed"?(o.error(l.error??"Security grading failed."),{exitCode:1,scanId:s}):{exitCode:0,scanId:s,reportUrl:G(c,s,l.reportUrl)}}return r.reportUrl?{exitCode:0,scanId:s,reportUrl:G(c,s,r.reportUrl)}:{exitCode:0,scanId:s,reportUrl:G(c,s)}}catch(r){return e.progress.finishLine(),o.error(r instanceof Error?r.message:String(r)),{exitCode:1}}}function ko(e){if(!e||e==="/")return"/";let t=e.trim();return(t.startsWith("/")?t:`/${t}`).replace(/\/+$/,"")||"/"}function os(e){return`Runtime baseline routes: ${e.join(", ")}`}function as(e,t=_o,o=ts){let a=new Set(["/"]);for(let n of e.runtimeRouteCandidates??[])a.add(ko(n));for(let n of e.policyCandidates??[])a.add(ko(n.path));return go([...a]).slice(0,t)}function vo(e){return`Collecting ${new URL(e.routeUrl).pathname} (${e.consentMode})`}function wo(e,t){let o=new URL(e.routeUrl).pathname;return{profileId:`${e.authenticated?"authenticated":"anonymous"}:${o}:${e.consentMode}`,routeUrl:e.routeUrl,consentMode:e.consentMode,authenticated:e.authenticated,evidence:t.evidence,consentInteraction:t.consentInteraction}}async function Po(e){let t=e.collect??bo,o=e.routeCap??_o,a=as(e.projectEvidence,o,e.pagesPerLevel);e.verbose&&(e.log??(l=>console.log(l)))(os(a));let n=ho({targetUrl:e.targetUrl,routes:a,authenticated:e.authenticated,routeCap:o}),i=[],r=[],s=[];for(let[c,l]of n.entries()){e.progress.runtimeProgress(c+1,n.length,vo(l));let u=await t({targetUrl:l.routeUrl,storageState:e.storageState,browserRuntime:e.browserRuntime,consentMode:l.consentMode,includeAccessibility:!1,...e.acceptLanguage?{acceptLanguage:e.acceptLanguage}:{}});if(r.push(wo(l,u)),q(u.characterization)){let d=new URL(l.routeUrl).pathname;u.evidence.consent.acceptPresent||s.push(`Consent banner on ${d} has no accept control; accept phase may be incomplete`),u.evidence.consent.rejectPresent||s.push(`Consent banner on ${d} has no reject control; decline phase may be incomplete`),i.push(...yo({targetUrl:e.targetUrl,routeUrl:l.routeUrl,authenticated:e.authenticated}))}else if(u.evidence.consent.bannerFound){let d=new URL(l.routeUrl).pathname;s.push(`Skipped consent variants for ${d}: banner detection was too weak to interact safely`)}else{let d=new URL(l.routeUrl).pathname;s.push(`Skipped consent variants for ${d}: no consent banner found`)}}for(let[c,l]of i.entries()){e.progress.runtimeProgress(c+1,i.length,vo(l));let u=await t({targetUrl:l.routeUrl,storageState:e.storageState,browserRuntime:e.browserRuntime,consentMode:l.consentMode,includeAccessibility:!1,...e.acceptLanguage?{acceptLanguage:e.acceptLanguage}:{}});r.push(wo(l,u))}return e.progress.success(`Runtime evidence collected (${r.length} profiles).`),{profiles:r,warnings:s}}var Ie=512e3;import{readdir as is,readFile as To,stat as rs}from"node:fs/promises";import Me from"node:path";var ss=new Set(["node_modules",".next","dist","build","coverage","out",".git",".vibemole",".cache",".turbo"]),Ao=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".yml",".yaml",".toml",".sql",".rules",".env",".local"]),jo=new Set([".env",".env.local",".env.development",".env.production",".env.test",".env.example",".env.sample",".gitignore",".dockerignore","Dockerfile","docker-compose.yml","docker-compose.yaml","firebase.json","firestore.rules","storage.rules","package.json","package-lock.json","pnpm-lock.yaml","yarn.lock","bun.lockb","next.config.js","next.config.mjs","next.config.ts"]),cs=new Set([".env",".env.local",".env.development",".env.production",".env.test",".env.example",".env.sample"]);function ls(e,t){let o=e.replace(/\\/g,"/");if(cs.has(t)||t.startsWith(".env."))return"env";if(t==="Dockerfile"||t.startsWith("docker-compose")||o.startsWith(".github/workflows/")&&(t.endsWith(".yml")||t.endsWith(".yaml")))return"devops";if(o.startsWith("supabase/")&&t.endsWith(".sql"))return"sql";if(t.endsWith(".rules")||t==="firebase.json")return"rules";if(t==="package.json"||t==="package-lock.json"||t==="pnpm-lock.yaml"||t==="yarn.lock"||t==="bun.lockb")return"manifest";if(t===".gitignore"||t===".dockerignore"||t.startsWith("next.config."))return"config";let a=Me.extname(t);return Ao.has(a)?a===".sql"?"sql":a===".json"||a===".yml"||a===".yaml"||a===".toml"?"config":"source":jo.has(t)?t==="Dockerfile"||t.startsWith("docker-compose")?"devops":"config":null}function us(e,t){if(jo.has(t)||t.startsWith(".env."))return!0;let o=e.replace(/\\/g,"/");if(o.startsWith("supabase/")&&t.endsWith(".sql")||o.startsWith(".github/workflows/")&&(t.endsWith(".yml")||t.endsWith(".yaml")))return!0;let a=Me.extname(t);return Ao.has(a)}async function ds(e){let t=await To(e);return t.subarray(0,Math.min(t.length,8192)).includes(0)}async function zo(e,t,o,a,n){let i;try{i=await is(t,{withFileTypes:!0})}catch{return}for(let r of i){let s=Me.join(t,r.name),c=Me.relative(e,s).replace(/\\/g,"/");if(r.isDirectory()){if(ss.has(r.name))continue;await zo(e,s,o,a,n);continue}if(!r.isFile()||!us(c,r.name))continue;let l=ls(c,r.name);if(!l)continue;let u;try{u=await rs(s)}catch{continue}if(u.size>Ie){a.push(c);continue}if(await ds(s)){n.push(c);continue}o.push({relativePath:c,absolutePath:s,sizeBytes:u.size,kind:l})}}async function Lo(e){let t=[],o=[],a=[],n=[];return await zo(e,e,t,o,a),t.sort((i,r)=>i.relativePath.localeCompare(r.relativePath)),{files:t,skippedLargeFiles:o,skippedBinaryFiles:a,warnings:n}}async function M(e){return To(e.absolutePath,"utf8")}var ms=[{prefix:"NEXT_PUBLIC_",ecosystemLabel:"Next.js"},{prefix:"NUXT_PUBLIC_",ecosystemLabel:"Nuxt"},{prefix:"EXPO_PUBLIC_",ecosystemLabel:"Expo"},{prefix:"REACT_APP_",ecosystemLabel:"Create React App"},{prefix:"VITE_",ecosystemLabel:"Vite"},{prefix:"GATSBY_",ecosystemLabel:"Gatsby"},{prefix:"PUBLIC_",ecosystemLabel:"SvelteKit / generic public"}],De=new Set(["NEXT_PUBLIC_SUPABASE_ANON_KEY","VITE_FIREBASE_API_KEY","NEXT_PUBLIC_FIREBASE_API_KEY"]),Uo="Client-exposed environment variable contains private-looking value",Io="Deployment workflow exposes private-looking value to frontend",Mo="(?:sk_(?:live|test)_|sk-(?:ant-|proj-)?|AIza|(?:AKIA|ASIA)|ghp_|github_pat_|gho_|ghu_|ghs_|ghr_)";function Do(e){for(let{prefix:t,ecosystemLabel:o}of ms)if(e.startsWith(t))return{envName:e,prefix:t,ecosystemLabel:o};return null}function ps(e){return new RegExp(Mo).test(e)}function Oo(e){let t=e.trim();if(!t||t.startsWith("#"))return null;let o=t.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!o?.[1])return null;let a=o[1],n=Do(a);if(!n||De.has(a))return null;let i=(o[2]??"").trim();if((i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),!ps(i))return null;let r=t.indexOf(a);return{...n,value:i,matchIndex:r>=0?r:0}}function No(e){let o=new RegExp(`\\b([A-Z][A-Z0-9_]*)\\s*[:=]\\s*['"]?(${Mo}[^\\s#'"]*)`).exec(e);if(!o?.[1]||!o[2])return null;let a=o[1],n=Do(a);return!n||De.has(a)?null:{...n,value:o[2],matchIndex:o.index}}function $o(e,t){return`${e.envName} (${e.ecosystemLabel}, ${e.prefix}) in ${t} uses a value that resembles a private API key.`}function Bo(e,t){return`${e}* variables (${t}) are exposed to the browser; values resembling private keys should be reviewed.`}function qo(e,t){return`${t} assigns a private-looking key to ${e.envName} (${e.ecosystemLabel}, ${e.prefix}).`}function Fo(e,t){return`CI workflows must not publish private key patterns to ${e}* variables (${t}) that reach browser bundles.`}function Oe(e){return{envName:e.envName,prefix:e.prefix,ecosystemLabel:e.ecosystemLabel}}var gs=[/your[_-]?key[_-]?here/i,/changeme/i,/replace[_-]?me/i,/\bexample\b/i,/\btest\b/i,/\bdummy\b/i,/\bplaceholder\b/i,/\bxxx+\b/i,/<secret>/i],fs=[{id:"secrets.stripe-secret-key",regex:/\bsk_(live|test)_[A-Za-z0-9]{8,}\b/g,title:"Stripe secret key",tag:"stripe"},{id:"secrets.openai-api-key",regex:/\bsk-proj-[A-Za-z0-9_-]{8,}\b/g,title:"OpenAI API key",tag:"openai"},{id:"secrets.anthropic-api-key",regex:/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,title:"Anthropic API key",tag:"anthropic"},{id:"secrets.openai-api-key",regex:/\bsk-(?!ant-|proj-)[A-Za-z0-9_-]{12,}\b/g,title:"OpenAI API key",tag:"openai"},{id:"secrets.google-api-key",regex:/\bAIza[A-Za-z0-9_-]{20,}\b/g,title:"Google API key",tag:"google"},{id:"secrets.aws-access-key",regex:/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,title:"AWS access key",tag:"aws"},{id:"secrets.github-token",regex:/\b(ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|gho_[A-Za-z0-9_]{20,}|ghu_[A-Za-z0-9_]{20,}|ghs_[A-Za-z0-9_]{20,}|ghr_[A-Za-z0-9_]{20,})\b/g,title:"GitHub token",tag:"github"},{id:"secrets.private-key-block",regex:/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/g,title:"Private key block",tag:"private-key"}],Ho=/\b(?:JWT_SECRET|SESSION_SECRET|AUTH_SECRET|NEXTAUTH_SECRET|SECRET_KEY)\s*=\s*['"]([^'"]{12,})['"]/gi;function Z(e){let t=e.trim();if(t.length<=4)return"***";let o=[{prefix:"sk_live_"},{prefix:"sk_test_"},{prefix:"sk-proj-"},{prefix:"sk-ant-"},{prefix:"sk-"},{prefix:"github_pat_"},{prefix:"ghp_"},{prefix:"gho_"},{prefix:"ghu_"},{prefix:"ghs_"},{prefix:"ghr_"},{prefix:"AKIA"},{prefix:"ASIA"},{prefix:"AIza"}];for(let{prefix:i}of o)if(t.startsWith(i)&&t.length>i.length+4)return`${i}***${t.slice(-4)}`;if(t.includes("PRIVATE KEY"))return"-----BEGIN *** PRIVATE KEY-----";let a=t.slice(-4);return`${t.slice(0,Math.min(6,t.length-4))}***${a}`}function hs(e){let t=e.trim();if(!t||t.startsWith("#"))return null;let o=t.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);if(!o?.[1])return null;let a=(o[2]??"").trim();return(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),{name:o[1],value:a}}function ys(e,t){let o=hs(e);return!o||!De.has(o.name)?!1:o.value.includes(t)}function bs(e){return gs.some(t=>t.test(e))}function Ct(e){return e.length<8||bs(e)||/^(true|false|null|undefined|\d+)$/i.test(e)?!1:/[A-Za-z0-9]/.test(e)}function _t(e,t,o,a){return{file:e,line:t,column:o,excerpt:Z(a),redactionApplied:!0}}function ks(e,t,o,a){let n=[],i=new Set;for(let c of fs){c.regex.lastIndex=0;let l;for(;(l=c.regex.exec(o))!==null;){let u=l[0];if(ys(o,u)||!Ct(u))continue;let d=`${c.id}:${t}:${l.index}`;i.has(d)||(i.add(d),n.push({id:c.id,category:"secrets",riskKind:"secret_exposure",status:"observed",confidence:"high",matchType:"regex",title:c.title,detail:`Matched ${c.tag} secret pattern in ${e}.`,reason:a?"Sample env file contains a value that looks like a real secret rather than a placeholder.":"Source file contains a value matching a known secret credential pattern.",sources:[_t(e,t,l.index+1,u)],tags:[c.tag,"secret-detector"]}))}}Ho.lastIndex=0;let r;for(;(r=Ho.exec(o))!==null;){let c=r[1]??"";if(!Ct(c))continue;let l=`secrets.hardcoded-auth-secret:${t}:${r.index}`;i.has(l)||(i.add(l),n.push({id:"secrets.hardcoded-auth-secret",category:"secrets",riskKind:"secret_exposure",status:"observed",confidence:"medium",matchType:"heuristic",title:"Hardcoded auth secret",detail:`Hardcoded session/JWT secret in ${e}.`,reason:"Environment or config assignment uses a literal value that looks like a real auth secret.",sources:[_t(e,t,r.index+1,c)],tags:["auth-secret","secret-detector"]}))}let s=Oo(o);if(s&&Ct(s.value)){let c=`secrets.next-public-private-pattern:${s.envName}:${t}`;i.has(c)||(i.add(c),n.push({id:"secrets.next-public-private-pattern",category:"frontend_exposure",riskKind:"secret_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:Uo,detail:$o(s,e),reason:Bo(s.prefix,s.ecosystemLabel),sources:[_t(e,t,s.matchIndex+1,s.value)],tags:["client-exposed-env",s.prefix.replace(/_$/,"").toLowerCase(),"secret-detector"],metadata:Oe(s)}))}return n}async function Vo(e){let t=[],o=new Set;for(let a of e){let i=(await M(a)).split(/\r?\n/),r=a.relativePath===".env.example"||a.relativePath===".env.sample"||a.relativePath.endsWith("/.env.example")||a.relativePath.endsWith("/.env.sample");for(let s=0;s<i.length;s+=1){let c=i[s]??"",l=ks(a.relativePath,s+1,c,r);for(let u of l){let d=`${u.id}:${u.sources.map(m=>`${m.file}:${m.line}`).join(",")}`;o.has(d)||(o.add(d),t.push(u))}}}return t}var Rt=/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;function de(e,t,o,a){return{file:e,line:t,column:a,excerpt:Z(o),redactionApplied:!0}}function vs(e,t){let o=[],a=t.split(/\r?\n/),n=new Set;for(let i=0;i<a.length;i+=1){let r=a[i]??"",s=i+1;if(/\bNEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-next-public:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-next-public",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"observed",confidence:"high",matchType:"exact",title:"Supabase service role exposed via NEXT_PUBLIC",detail:`${e} references NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY.`,reason:"Service role keys must not use NEXT_PUBLIC_* names because they are bundled for the browser.",sources:[de(e,s,r.trim())],tags:["supabase","service-role","next-public"]}))}if(/\bSUPABASE_SERVICE_ROLE_KEY\b/.test(r)&&!/\bNEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-reference:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-reference",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"medium",matchType:"exact",title:"Supabase service role key reference",detail:`${e} references SUPABASE_SERVICE_ROLE_KEY.`,reason:"Privileged Supabase service role material was referenced; verify it is server-only and not committed.",sources:[de(e,s,r.trim())],tags:["supabase","service-role"]}))}if(/\bservice_role\b/i.test(r)&&!/\bSUPABASE_SERVICE_ROLE_KEY\b/.test(r)){let c=`baas.supabase-service-role-reference:literal:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-reference",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Supabase service_role reference",detail:`${e} mentions service_role.`,reason:"Literal service_role references may indicate privileged Supabase client configuration.",sources:[de(e,s,r.trim())],tags:["supabase","service-role"]}))}if(/\bNEXT_PUBLIC_[A-Z0-9_]*SUPABASE[A-Z0-9_]*\b/.test(r)&&Rt.test(r)&&!/\bNEXT_PUBLIC_SUPABASE_ANON_KEY\b/.test(r)){Rt.lastIndex=0;let c=`baas.supabase-jwt-frontend-env:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-next-public",category:"frontend_exposure",riskKind:"unsafe_baas_key_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"JWT-like Supabase value in frontend env",detail:`${e} assigns a JWT-like value to a NEXT_PUBLIC Supabase variable.`,reason:"JWT-shaped values in NEXT_PUBLIC Supabase env names may expose privileged credentials to the browser.",sources:[de(e,s,r.trim())],tags:["supabase","jwt","next-public"]})),Rt.lastIndex=0}if(/createClient\s*\([^)]*(?:service_role|SUPABASE_SERVICE_ROLE_KEY)/i.test(r)){let c=`baas.supabase-service-role-client-exposure:${e}:${s}`;n.has(c)||(n.add(c),o.push({id:"baas.supabase-service-role-client-exposure",category:"baas_security",riskKind:"unsafe_baas_key_exposure",status:"observed",confidence:"high",matchType:"heuristic",title:"Supabase client configured with service role",detail:`${e} passes service role material into createClient.`,reason:"createClient should use anon keys in browser-facing code; service role bypasses RLS.",sources:[de(e,s,r.trim())],tags:["supabase","createClient","service-role"]}))}}return o}async function Ko(e){let t=e.filter(n=>n.kind==="source"||n.kind==="env"||n.kind==="config"||n.kind==="sql"||n.kind==="devops"),o=[],a=new Set;for(let n of t){let i=await M(n);for(let r of vs(n.relativePath,i)){let s=`${r.id}:${r.sources.map(c=>`${c.file}:${c.line}`).join(",")}`;a.has(s)||(a.add(s),o.push(r))}}return o}function me(e){let t=e.replace(/\\/g,"/"),o=t.split("/").filter(Boolean);return o[o.length-1]??t}function ws(e,t){let o=t.startsWith("/")?t:`/${t}`,a=[];for(let n of e){let i=n.replace(/\\/g,"/");(i===t.replace(/^\//,"")||i.endsWith(o))&&a.push(n)}return a.sort((n,i)=>n.localeCompare(i))}function Q(e,t){return ws(e,t)[0]}function Ne(e,t){let o=[];for(let a of e)me(a)===t&&o.push(a);return o.sort((a,n)=>a.localeCompare(n))}function ee(e,t){return Ne(e,t)[0]}var xs=[".env",".env.local",".env.production",".env.development.local"],Ss=["package-lock.json","pnpm-lock.yaml","yarn.lock","bun.lockb"],Es=[/\bsk_(live|test)_[A-Za-z0-9]{8,}\b/,/\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{12,}\b/,/\bAIza[A-Za-z0-9_-]{20,}\b/,/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/,/\b(ghp_|github_pat_|gho_|ghu_|ghs_|ghr_)[A-Za-z0-9_]{20,}\b/];function Cs(e){return new Map(e.map(t=>[t.relativePath,t]))}function _s(e){return e?e.split(/\r?\n/).map(a=>a.trim()).filter(a=>a&&!a.startsWith("#")).some(a=>a===".env"||a===".env*"||a===".env.*"):!1}function Rs(e){return/^\s*USER\s+(?!root\b)/im.test(e)}async function pe(e,t){let o=e.get(t)??[...e.values()].find(a=>me(a.relativePath)===me(t));return o?M(o):null}async function Wo(e,t){let o=Cs(t),a=[],n=Q(o.keys(),".gitignore")??ee(o.keys(),".gitignore"),i=Q(o.keys(),".dockerignore")??ee(o.keys(),".dockerignore"),r=Q(o.keys(),"Dockerfile")??ee(o.keys(),"Dockerfile"),s=Q(o.keys(),"package.json")??ee(o.keys(),"package.json"),c=n?await pe(o,n):null,l=i?await pe(o,i):null,u=r?await pe(o,r):null,d=s?await pe(o,s):null,m=xs.filter(f=>o.has(f)||[...o.keys()].some(w=>me(w)===f));m.length>0&&!_s(c)&&a.push({id:"devops.env-not-gitignored",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"high",matchType:"absence",title:"Env files may not be gitignored",detail:`Found ${m.join(", ")} but .gitignore does not ignore .env or .env*.`,reason:"Environment files with secrets often exist in the repo; missing .env gitignore patterns increase accidental commit risk.",sources:m.map(f=>({file:Q(o.keys(),f)??ee(o.keys(),f)??f,line:1})),tags:["gitignore","env"],metadata:{envFiles:m}});let g=!!u,y=!!l;g&&!y&&a.push({id:"devops.dockerignore-missing",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"medium",matchType:"absence",title:".dockerignore is missing",detail:"Dockerfile exists but no .dockerignore was found.",reason:"Without .dockerignore, Docker builds may copy local artifacts (including .env or .git) into images.",sources:[{file:r??"Dockerfile",line:1}],tags:["docker","dockerignore"]}),g&&u&&/COPY\s+\.\s+\./i.test(u)&&!y&&a.push({id:"devops.docker-copy-all-without-dockerignore",category:"devops_deployment",riskKind:"deployment_leakage",status:"observed",confidence:"high",matchType:"heuristic",title:"Dockerfile copies entire build context",detail:"Dockerfile uses COPY . . without a .dockerignore file.",reason:"COPY . . can bundle secrets and VCS metadata into images when .dockerignore is absent.",sources:[{file:r??"Dockerfile",line:u.split(/\r?\n/).findIndex(f=>/COPY\s+\.\s+\./i.test(f))+1||1}],tags:["docker","copy"]}),g&&u&&!Rs(u)&&a.push({id:"devops.docker-non-root-user-missing",category:"devops_deployment",riskKind:"deployment_leakage",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Dockerfile may run as root",detail:"Dockerfile does not declare a non-root USER instruction.",reason:"Containers that run as root increase blast radius if the workload is compromised.",sources:[{file:r??"Dockerfile",line:1}],tags:["docker","user"]});let v=t.filter(f=>f.kind==="devops"&&f.relativePath.startsWith(".github/workflows/"));for(let f of v){let P=(await M(f)).split(/\r?\n/);for(let j=0;j<P.length;j+=1){let T=P[j]??"",_=j+1;Es.some($=>$.test(T))&&a.push({id:"devops.github-actions-hardcoded-secret",category:"devops_deployment",riskKind:"secret_exposure",status:"observed",confidence:"high",matchType:"regex",title:"Hardcoded secret in GitHub Actions workflow",detail:`${f.relativePath} contains a hardcoded secret-like value.`,reason:"CI workflow files should reference GitHub secrets instead of embedding credential material.",sources:[{file:f.relativePath,line:_,excerpt:Z(T.trim()),redactionApplied:!0}],tags:["github-actions","ci"]}),/\becho\b.*\b(?:SECRET|TOKEN|API_KEY|PASSWORD)\b/i.test(T)&&a.push({id:"devops.github-actions-echo-secret-env",category:"devops_deployment",riskKind:"deployment_leakage",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"Workflow echoes secret-like env names",detail:`${f.relativePath} echoes environment variables that look like secrets.`,reason:"Echoing secret env names in CI logs can leak values when shell tracing or expansion is enabled.",sources:[{file:f.relativePath,line:_,excerpt:T.trim().slice(0,120)}],tags:["github-actions","echo"]});let C=No(T);C&&a.push({id:"devops.github-actions-next-public-private",category:"frontend_exposure",riskKind:"secret_exposure",status:"needs_review",confidence:"medium",matchType:"heuristic",title:Io,detail:qo(C,f.relativePath),reason:Fo(C.prefix,C.ecosystemLabel),sources:[{file:f.relativePath,line:_,excerpt:Z(T.trim()),redactionApplied:!0}],tags:["github-actions","client-exposed-env",C.prefix.replace(/_$/,"").toLowerCase()],metadata:Oe(C)})}}let b=Ss.flatMap(f=>Ne(o.keys(),f));b.length>1&&a.push({id:"devops.multiple-lockfiles",category:"dependencies",riskKind:"supply_chain_hygiene",status:"needs_review",confidence:"medium",matchType:"heuristic",title:"Multiple package lockfiles detected",detail:`Found lockfiles: ${b.join(", ")}.`,reason:"Multiple lockfiles can cause inconsistent dependency resolution across environments.",sources:b.map(f=>({file:f,line:1})),tags:["lockfile","dependencies"],metadata:{lockfiles:[...b]}}),d&&b.length===0&&a.push({id:"devops.lockfile-missing",category:"dependencies",riskKind:"supply_chain_hygiene",status:"observed",confidence:"high",matchType:"absence",title:"package.json without lockfile",detail:"package.json exists but no package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lockb was found.",reason:"Missing lockfiles make dependency installs non-deterministic and harder to audit.",sources:[{file:s??"package.json",line:1}],tags:["lockfile","dependencies"]});let x=["next.config.js","next.config.mjs","next.config.ts"].flatMap(f=>Ne(o.keys(),f));for(let f of x){let w=await pe(o,f);if(w&&/productionBrowserSourceMaps\s*:\s*true/.test(w)){let P=w.split(/\r?\n/).findIndex(j=>/productionBrowserSourceMaps\s*:\s*true/.test(j))+1;a.push({id:"devops.production-browser-source-maps",category:"frontend_exposure",riskKind:"deployment_leakage",status:"needs_review",confidence:"low",matchType:"heuristic",title:"Production browser source maps enabled",detail:`${f} sets productionBrowserSourceMaps: true.`,reason:"Source maps can be legitimate for debugging but should be reviewed for production exposure of client-side code.",sources:[{file:f,line:P||1}],tags:["nextjs","source-maps"]})}}return a}function Ps(e){let t=new Set,o=[];for(let a of e){let n=[a.id,a.category,...a.sources.map(i=>`${i.file}:${i.line}:${i.column??0}`)].join("|");t.has(n)||(t.add(n),o.push(a))}return o}function Ts(e){return[...e].sort((t,o)=>{let a=t.category.localeCompare(o.category);if(a!==0)return a;let n=t.id.localeCompare(o.id);if(n!==0)return n;let i=t.sources[0]?.file??"",r=o.sources[0]?.file??"",s=i.localeCompare(r);return s!==0?s:(t.sources[0]?.line??0)-(o.sources[0]?.line??0)})}function As(e){let t=0,o=0,a=0;for(let n of e)n.status==="observed"&&(t+=1),n.status==="needs_review"&&(o+=1),n.status==="not_applicable"&&(a+=1);return{signalsObserved:t,needsReview:o,notApplicable:a}}async function Go(e){let{root:t,scanId:o,generatedAt:a=new Date().toISOString()}=e,n=await Lo(t),i=await Vo(n.files),r=await Ko(n.files),s=await Wo(t,n.files),c=[{id:"secrets",status:"completed",filesScanned:n.files.length,signals:i.length,warnings:[]},{id:"baas",status:"completed",filesScanned:n.files.length,signals:r.length,warnings:[]},{id:"devops",status:"completed",filesScanned:n.files.length,signals:s.length,warnings:n.warnings}],l=Ts(Ps([...i,...r,...s])),u=As(l),d=await Promise.all(n.files.map(async m=>({path:m.relativePath,content:await M(m)})));return{version:_e,scanId:o,generatedAt:a,root:t,summary:{filesScanned:n.files.length,filesSkipped:n.skippedLargeFiles.length+n.skippedBinaryFiles.length,signalsObserved:u.signalsObserved,needsReview:u.needsReview,notApplicable:u.notApplicable},detectors:c,signals:l,repoSnapshot:{paths:n.files.map(m=>m.relativePath),files:d},diagnostics:{maxFileBytes:Ie,skippedLargeFiles:n.skippedLargeFiles,skippedBinaryFiles:n.skippedBinaryFiles,warnings:n.warnings}}}import{mkdir as js,writeFile as zs}from"node:fs/promises";import Yo from"node:path";async function Xo(e,t){let o=Yo.join(e,".vibemole","security");await js(o,{recursive:!0});let a=Yo.join(o,"evidence.json");return await zs(a,`${JSON.stringify(t,null,2)}
|
|
2567
|
+
`,"utf8"),a}var Is=Jo.dirname(Us(import.meta.url)),Ms=Jo.resolve(Is,"../../..");function Ds(e){return!e&&!!(D.stdin.isTTY&&D.stdout.isTTY)}async function Os(){let e;try{e=Wt(D.argv.slice(2))}catch(s){return console.error(s instanceof Error?s.message:String(s)),console.error(Ye()),1}if(e.command==="help")return console.log(Ye()),0;if(e.command==="login"){let s=`${D.env.USER||"user"}@${Ls.hostname()}`;return await Ee(e.webUrl,s),0}if(e.command==="logout")return await gn(),console.log("Logged out of VibeMole CLI."),0;if(e.command==="whoami"){let s=await H(),c=xe(s,e);if("error"in c)return console.error(c.error),1;if(se(s))return console.error("CLI session expired. Run: vibemole login"),1;let l=await pn(c.webUrl,s.token);return console.log(`userId: ${l.userId}`),console.log(`tokenPrefix: ${l.tokenPrefix}`),console.log(`webUrl: ${s.webUrl}`),0}let t=D.cwd(),o=Ds(e.json),a=new Pe(D.stderr,!e.json),n=e.url,i=null,r=0;try{let s;if(await Et(a,e.verbose,{start:xo,success:So},async()=>{s=await Sn(t)}),!n&&!e.noRuntime){let v=await En({root:t,onOutput:e.verbose?b=>D.stderr.write(b):void 0});n=v.targetUrl,i=v.process,n||(n=await $n({interactive:o,ask:ie,log:b=>console.error(b)}))}let c=[],l=[],u=!1;if(!e.noRuntime&&n){let v=await Ue();if(!v.available&&o&&await ae("No browser runtime was found. Install Playwright Chromium now to run runtime checks?",!0)&&(await Yt({repoRoot:Ms,cwd:t}),v=await Ue()),v.available){let b=await an({root:t,targetUrl:n,noLogin:e.noLogin,loginRequired:e.loginRequired,loginMethod:e.loginMethod,loginRefresh:e.loginRefresh,interactive:o});u=b.authenticated,!b.authenticated&&e.loginRequired&&e.verbose&&console.error(`Authenticated scan not available: ${b.reason}`);let x=await Po({targetUrl:n,projectEvidence:s,authenticated:u,storageState:b.storageState,browserRuntime:v,progress:a,routeCap:e.runtimeRouteCap,pagesPerLevel:e.runtimePagesPerLevel,acceptLanguage:ve(e.locale),verbose:e.verbose});c=x.profiles,l=x.warnings}else console.error("Browser runtime unavailable; evidence package will include project and policy-route evidence only.")}let d,m;await Et(a,e.verbose,{start:Eo,success:Co},async()=>{d=await On({root:t,targetUrl:n,reportLocale:e.locale,collectorRouteCap:e.runtimeRouteCap,projectEvidence:s,runtimeProfiles:c,collectorWarnings:l}),m=await Dn(t,d)});let g=null;e.security&&(g=await Go({root:t,scanId:d.scanId}),e.noUpload&&await Xo(t,g));let y=null;if(e.noUpload||(y=await Ro({args:e,evidencePackage:d,securityEvidence:g,progress:a,...e.security?{forceUpload:!0}:{}}),r=y.exitCode),e.json){let v=Te(d,g);e.security&&!e.noUpload&&y?console.log(JSON.stringify({...v,scanId:y.scanId??d.scanId,...y.reportUrl?{reportUrl:y.reportUrl}:{}},null,2)):console.log(JSON.stringify(v,null,2))}else if(r===0){a.finishLine();for(let v of Xn({uploadOutcome:e.noUpload?"skipped_no_upload":"uploaded",evidenceFilePath:m,reportUrl:y?.reportUrl}).split(`
|
|
2568
|
+
`))console.log(v)}}finally{i?.kill()}return r}Os().then(e=>{D.exitCode=e}).catch(e=>{console.error(e instanceof Error?e.message:String(e)),D.exitCode=1});
|