@seanmozeik/tripwire 0.7.2 → 0.9.0

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.
Files changed (46) hide show
  1. package/README.md +20 -2
  2. package/dist/index.js +22 -27
  3. package/dist/tripwire-cli.js +1 -1
  4. package/dist/tripwire-hook.js +1 -1
  5. package/dist/tripwire-pi.js +16 -6
  6. package/dist/tripwire.js +109 -121
  7. package/dist/types/dispatch.d.ts +4 -2
  8. package/dist/types/index.d.ts +1 -1
  9. package/dist/types/lib/bash/analyze.d.ts +3 -0
  10. package/dist/types/lib/bash/carriers.d.ts +11 -0
  11. package/dist/types/lib/bash/config.d.ts +17 -0
  12. package/dist/types/lib/bash/execution-types.d.ts +13 -0
  13. package/dist/types/lib/bash/execution.d.ts +13 -0
  14. package/dist/types/lib/bash/index.d.ts +8 -0
  15. package/dist/types/lib/bash/pipeline-inputs.d.ts +10 -0
  16. package/dist/types/lib/bash/static-command.d.ts +9 -0
  17. package/dist/types/lib/bash/types.d.ts +64 -0
  18. package/dist/types/lib/bash/values.d.ts +25 -0
  19. package/dist/types/lib/bash/wrappers.d.ts +21 -0
  20. package/dist/types/lib/code/analyze.d.ts +3 -0
  21. package/dist/types/lib/code/calls.d.ts +8 -0
  22. package/dist/types/lib/code/carriers.d.ts +18 -0
  23. package/dist/types/lib/code/containers.d.ts +4 -0
  24. package/dist/types/lib/code/control.d.ts +13 -0
  25. package/dist/types/lib/code/data.d.ts +9 -0
  26. package/dist/types/lib/code/imports.d.ts +5 -0
  27. package/dist/types/lib/code/members.d.ts +4 -0
  28. package/dist/types/lib/code/syntax.d.ts +10 -0
  29. package/dist/types/lib/code/types.d.ts +51 -0
  30. package/dist/types/lib/code/uv.d.ts +13 -0
  31. package/dist/types/lib/code/values.d.ts +9 -0
  32. package/dist/types/lib/config.d.ts +32 -0
  33. package/dist/types/lib/event.d.ts +7 -1
  34. package/dist/types/lib/rule-actions.d.ts +27 -0
  35. package/dist/types/rules/bash-bypass.d.ts +5 -0
  36. package/dist/types/rules/bash-deny.d.ts +4 -4
  37. package/dist/types/rules/bash-git.d.ts +2 -2
  38. package/dist/types/rules/bash-network-install.d.ts +2 -2
  39. package/dist/types/rules/bash-redirect.d.ts +2 -2
  40. package/dist/types/rules/bash-scoped-rm.d.ts +2 -2
  41. package/dist/types/rules/bash-tar-explosion.d.ts +2 -2
  42. package/dist/types/rules/config-custom.d.ts +3 -3
  43. package/dist/types/rules/embedded-code.d.ts +15 -0
  44. package/dist/types/rules/tool-policy.d.ts +2 -2
  45. package/package.json +11 -8
  46. package/dist/types/lib/bash.d.ts +0 -27
package/dist/index.js CHANGED
@@ -1,35 +1,30 @@
1
1
  // @bun
2
- var fe={allow:0,warn:1,ask:2,deny:3},a=(e)=>({kind:"allow",rule:e,message:""}),x=(e,t)=>({kind:"warn",rule:e,message:t}),y=(e,t)=>({kind:"ask",rule:e,message:t}),d=(e,t)=>({kind:"deny",rule:e,message:t}),D=(e)=>{let t=a("none");for(let n of e)if(fe[n.kind]>fe[t.kind])t=n;return t};import{BunRuntime as ps}from"@effect/platform-bun";import{Cause as L,Data as mt,Effect as S,Exit as J,Schema as q}from"effect";import{parse as xt,quote as H}from"shell-quote";var Et=["dist","build","_build","out","target",".next",".nuxt",".svelte-kit",".output",".astro",".angular",".vite",".parcel-cache",".turbo",".vercel",".netlify",".fly",".wrangler",".serverless","coverage",".nyc_output",".cache",".ruff_cache",".mypy_cache",".pytest_cache",".ty_cache",".tox","__pycache__",".venv","venv","node_modules",".gradle","DerivedData",".bundle",".cargo-target","tmp",".tmp",".state",".terraform",".yarn/cache",".yarn/install-state.gz",".pnpm-store",".bun"],ye=["/tmp","/var/tmp","/var/folders","/private/tmp","/private/var/tmp","/private/var/folders"],ke=new Set([">",">>","<","<<","<<<","<>",">&","<&","&>","&>>"]),be=new Set([";","&&","||","|","|&","&"]),Se=(e)=>typeof e==="string",E=(e)=>{if(typeof e==="object"&&"op"in e&&typeof e.op==="string")return e.op;return null},_t=(e)=>typeof e==="object"&&("comment"in e),Ct=(e)=>{try{let t=[...new Bun.Glob(e).scanSync({onlyFiles:!1,dot:!0})];if(t.length>0)return t}catch{}return[e]},Pt=(e)=>{if(Se(e))return[e];if(typeof e==="object"&&"op"in e&&e.op==="glob"&&"pattern"in e)return Ct(String(e.pattern));let t=E(e);if(t!==null){if(ke.has(t)||be.has(t))return[];return[`__op_${t}__`]}if(_t(e))return[];return["__tripwire_cmd_sub__"]},ge=(e,t)=>{let n=[],r=[],o=[],s=[],i=0;while(i<e.length){let g=e[i],p=E(g);if(p!==null&&ke.has(p)){let w=n.at(-1);if(w!==void 0&&/^[0-9]+$/.test(w)&&t.remaining>0)n.pop(),t.remaining-=1;let C=e[i+1];if(C!==void 0&&Se(C)){s.push({op:p,target:C}),i+=2;continue}i+=1;continue}for(let w of Pt(g))n.push(w);i+=1}if(n.length===0)return null;for(let g=1;g<n.length;g+=1){let p=n[g];if(p.startsWith("-")&&p!=="-")o.push(p);else r.push(p)}let l=n[0],u=l.lastIndexOf("/");return{head:u===-1?l:l.slice(u+1),tokens:n,args:r,flags:o,redirects:s,raw:n.join(" ")}},Ht=(e)=>`$${e}`,Tt=(e)=>{let t=[];for(let n=0;n<e.length;n+=1){let r=e[n],o=e[n+1];if(E(r)==="&"&&o!==void 0&&(E(o)===">"||E(o)===">>")){let s={op:E(o)===">"?"&>":"&>>"};t.push(s),n+=1;continue}if(E(r)===">"&&o!==void 0&&E(o)==="|"){t.push({op:">"}),n+=1;continue}t.push(r)}return t},Dt=(e)=>e.match(/(?<![\w$])\d+(?=[<>])/g)?.length??0,K=(e)=>{let t=/<<-?\s*(?:"(?<quoted>[^"]+)"|'(?<single>[^']+)'|(?<unquoted>[A-Za-z_][A-Za-z0-9_]*))/u.exec(e);return t?.groups?.quoted??t?.groups?.single??t?.groups?.unquoted??null},It=/(?:^|[|;&]\s*)(?:\/(?:usr\/bin|bin|usr\/local\/bin|opt\/homebrew\/bin)\/)?(?:sh|bash|zsh|dash|ksh|ash)(?:\s|$)/u,Z=(e)=>It.test(e),we=(e)=>{let t=e.split(`
3
- `),n=[];for(let r=0;r<t.length;r+=1){let o=t[r];n.push(o);let s=K(o);if(s===null||Z(o))continue;r+=1;while(r<t.length&&t[r].trim()!==s)r+=1;if(r<t.length)n.push("__HEREDOC_BODY__",t[r])}return n.join(`
4
- `)},Re=(e)=>{let t=new Map,n=e.split(`
5
- `);for(let r=0;r<n.length;r+=1){let o=n[r],s=K(o);if(s===null||Z(o))continue;let i=[];r+=1;while(r<n.length&&n[r].trim()!==s)i.push(n[r]),r+=1;if(!t.has(s))t.set(s,i.join(`
6
- `))}return t},Ft=(e)=>{let t=e.split(`
7
- `),n=[];for(let r=0;r<t.length;r+=1){let o=t[r],s=K(o);if(s===null||!Z(o))continue;let i=[];r+=1;while(r<t.length&&t[r].trim()!==s)i.push(t[r]),r+=1;if(i.length>0)n.push(i.join(`
8
- `))}return n},$t=(e,t)=>{for(let n=t;n<e.length;n+=1){let r=e[n];if(r==="\\"){n+=1;continue}if(r==="`")return n}return null},At=(e,t)=>{let n=1,r=null;for(let o=t;o<e.length;o+=1){let s=e[o];if(s==="\\"){o+=1;continue}if(r==="single"){if(s==="'")r=null;continue}if(s==="'"){r??="single";continue}if(s==='"'){r=r==="double"?null:"double";continue}if(s==="("){n+=1;continue}if(s===")"){if(n-=1,n===0)return o}}return null},Wt=(e)=>{let t=[],n=null;for(let r=0;r<e.length;r+=1){let o=e[r];if(o==="\\"){r+=1;continue}if(n==="single"){if(o==="'")n=null;continue}if(o==="'"){n??="single";continue}if(o==='"'){n=n==="double"?null:"double";continue}if(o==="`"){let f=$t(e,r+1);if(f!==null)t.push(e.slice(r+1,f)),r=f;continue}let s=e[r+1];if(!(o==="$"&&s==="(")&&!(n===null&&(o==="<"||o===">")&&s==="("))continue;let u=At(e,r+2);if(u!==null)t.push(e.slice(r+2,u)),r=u}return t},ve=/^\$\{?HOME\}?(?:\/|$)/,xe=(e)=>{if(e===""||e==="-")return!1;if(e==="/"||e==="~"||e==="."||e==="..")return!0;if(e.startsWith("/")||e.startsWith("~")||e.startsWith("./")||e.startsWith("../")||ve.test(e))return!0;return!1},M=(e)=>{if(e==="/")return 100;if(e==="~"||ve.test(e))return 90;if(/^\/(?<dir>etc|usr|bin|sbin|System|Library|var|boot|root|home)(?<suffix>\/|$)/.test(e))return 80;if(e.startsWith("/Users/"))return 70;if(e.startsWith("/")||e.startsWith("~"))return 60;if(e.startsWith("../"))return 40;if(e===".."||e==="."||e.startsWith("./"))return 10;return 50},Bt=new Set(["-e","--extension","-t","--type","-E","--exclude","-d","--max-depth","--min-depth","--exact-depth","-c","--color","--changed-within","--changed-before","-S","--size","-o","--owner","-j","--threads","-g","--glob","--format","--max-results","--ignore-file","--search-path","--base-directory","--path-separator","--and"]),Ot=(e,t)=>{let n=[],r=1;while(r<t){let o=e[r];if(Bt.has(o)){r+=2;continue}if(o.startsWith("-")){r+=1;continue}if(xe(o))n.push(o);r+=1}if(n.length===0)return".";return n.sort((o,s)=>M(s)-M(o)),n[0]},Lt=(e,t)=>{let n=[];for(let r=1;r<t;r+=1){let o=e[r];if(o.startsWith("-"))break;if(xe(o))n.push(o)}if(n.length===0)return".";return n.sort((r,o)=>M(o)-M(r)),n[0]},he={execFlags:new Set(["-x","-X","--exec","--exec-batch"]),placeholders:new Set(["{}","{/}","{//}","{.}","{/.}"]),pickRoot:Ot},me={execFlags:new Set(["-exec","-execdir","-ok","-okdir"]),placeholders:new Set(["{}"]),pickRoot:Lt},Mt=Object.assign(Object.create(null),{fd:he,fdfind:he,find:me,gfind:me}),Nt=(e,t,n)=>e.map((r)=>t.placeholders.has(r)?n:r),jt=(e)=>{let t=Mt[e.head];if(t===void 0)return[];let n=[],{tokens:r}=e;for(let o=1;o<r.length;o+=1){if(!t.execFlags.has(r[o]))continue;let s=[],i=o+1;while(i<r.length){let f=r[i];if(f===";"||f==="+")break;s.push(f),i+=1}if(s.length===0)continue;let l=s[0];if(t.placeholders.has(l))continue;let u=t.pickRoot(r,o);n.push(H(Nt(s,t,u))),o=i}return n},Ut=/\$\(\s*cat\s+<<-?\s*['"]?(?<delimiter>\w+)['"]?\s*\n(?<body>[\s\S]*?)\n\s*\k<delimiter>\s*\)/u,zt=/\$\(\s*(?:printf|echo)\s+(?:-[a-zA-Z]+\s+)*'(?<content>[^']*)'/u,j=(e,t)=>{let n=Ut.exec(e);if(n!==null){let o=n.groups?.body??e;if(t!==void 0&&o.trim()==="__HEREDOC_BODY__"){let s=n.groups?.delimiter,i=s===void 0?void 0:t.get(s);if(i!==void 0)return i}return o}let r=zt.exec(e);if(r!==null)return r.groups?.content??e;return e},Gt=new Set(["sh","bash","zsh","dash","ksh","ash"]),Jt=(e)=>{if(!Gt.has(e.head))return[];let{tokens:t}=e;for(let n=1;n<t.length;n+=1){let r=t[n];if(r==="-c"&&n+1<t.length)return[t[n+1]];if(r.startsWith("-")&&!r.startsWith("--")&&r.endsWith("c")&&r.length>2&&n+1<t.length)return[t[n+1]]}return[]},qt=new Set(["command","exec","env","time","nohup","setsid","nice","ionice","chronic","stdbuf","unbuffer","script","taskset","sudo","doas","xargs","watch"]),Vt={command:new Set,env:new Set(["-u","--unset","-C","--chdir","-S","--split-string","--block-signal"]),time:new Set(["-f","--format","-o","--output"]),nice:new Set(["-n","--adjustment"]),ionice:new Set(["-c","--class","-n","--classdata","-p","--pid"]),stdbuf:new Set(["-i","--input","-o","--output","-e","--error"]),script:new Set(["-c","--command"]),taskset:new Set,sudo:new Set(["-u","--user","-g","--group","-C","--close-from","-D","--chdir","-h","--host","-p","--prompt","-r","--role","-t","--type","-U","--other-user","-R","--chroot","-T","--command-timeout"]),doas:new Set(["-a","-C","-u"]),xargs:new Set(["-I","-i","-J","-n","--max-args","-P","--max-procs","-s","--max-chars","-L","--max-lines","-E","--eof","-d","--delimiter","-a","--arg-file","--replace"]),watch:new Set(["-n","--interval"])},Kt=(e)=>/^[A-Za-z_][A-Za-z0-9_]*=.*/u.test(e),Zt=(e)=>{let t=e[0],n=Vt[t]??new Set,r=1;while(r<e.length){let o=e[r];if(t==="env"&&Kt(o)){r+=1;continue}if(n.has(o)){r+=2;continue}if(o.includes("=")&&n.has(o.slice(0,o.indexOf("=")))){r+=1;continue}if(o.startsWith("--")&&o!=="--"){r+=1;continue}if(o.startsWith("-")&&o!=="-"){r+=1;continue}break}return r},Xt=(e)=>{if(!qt.has(e.head))return[];if(e.head==="script")for(let r=1;r<e.tokens.length-1;r+=1){let o=e.tokens[r];if(o==="-c"||o==="--command")return[e.tokens[r+1]];if(o.startsWith("--command="))return[o.slice(10)]}let t=Zt(e.tokens),n=H(e.tokens.slice(t));return n===""?[]:[n]},Yt=new Set(["eval"]),Qt=(e)=>{if(!Yt.has(e.head))return[];if(e.tokens.length===2)return[e.tokens[1]];let t=H(e.tokens.slice(1));return t===""?[]:[t]},en=new Set(["run","proxy","err","test","summary"]),tn=(e)=>e==="rtk",nn=(e)=>{let t=1;while(t<e.length){let n=e[t];if(n.startsWith("-")&&n!=="-"&&n!=="--"){t+=1;continue}break}return t},Ee=(e,t)=>{for(let n=t;n<e.length;n+=1){let r=e[n];if((r==="-c"||r==="--command")&&n+1<e.length)return e[n+1];if(r.startsWith("--command="))return r.slice(10)}return null},rn=(e)=>{if(!tn(e.head))return[];let t=nn(e.tokens),n=e.tokens[t];if(n===void 0)return[];if(en.has(n)){let o=Ee(e.tokens,t+1);if(o!==null)return o===""?[]:[o];let s=t+1;while(s<e.tokens.length){let l=e.tokens[s];if(l==="--"){s+=1;break}if(l.startsWith("-")&&l!=="-"){s+=1;continue}break}let i=H(e.tokens.slice(s));return i===""?[]:[i]}let r=H(e.tokens.slice(t));return r===""?[]:[r]},on={timeout:{valueFlags:new Set(["-s","--signal","-k","--kill-after"]),skipPositionals:1,dashCommand:!1},gtimeout:{valueFlags:new Set(["-s","--signal","-k","--kill-after"]),skipPositionals:1,dashCommand:!1},chroot:{valueFlags:new Set(["--userspec","--groups"]),skipPositionals:1,dashCommand:!1},flock:{valueFlags:new Set(["-w","--wait","--timeout","-E","--conflict-exit-code"]),skipPositionals:1,dashCommand:!0},su:{valueFlags:new Set,skipPositionals:0,dashCommand:!0}},sn=(e)=>{let t=on[e.head];if(t===void 0)return[];if(t.dashCommand){let o=Ee(e.tokens,1);if(o!==null)return o===""?[]:[o]}let n=1;while(n<e.tokens.length){let o=e.tokens[n];if(t.valueFlags.has(o)){n+=2;continue}if(o.includes("=")&&t.valueFlags.has(o.slice(0,o.indexOf("=")))){n+=1;continue}if(o==="--"){n+=1;break}if(o.startsWith("-")&&o!=="-"){n+=1;continue}break}n+=t.skipPositionals;let r=H(e.tokens.slice(n));return r===""?[]:[r]},_e=new Set(["!","if","elif","then","else","while","until","do"]),an=(e)=>{if(!_e.has(e.head)||e.tokens.length<2)return[];return[H(e.tokens.slice(1))]},cn=[jt,Jt,Xt,Qt,rn,sn,an],N="__tripwire_unsupported_shell__",pe=(e)=>({head:N,tokens:[N],args:[],flags:[],redirects:[],raw:e}),ln=(e)=>e.some((t)=>{if(t.head==="case"||t.head==="function"||t.head==="{")return!0;if(t.tokens.includes("__op_(__")||t.tokens.includes("__op_)__"))return!0;return _e.has(t.head)&&t.tokens[1]==="{"}),dn=(e)=>{let t="",n=!1,r=!1,o=!1;for(let s of e){if(o){t+=s,o=!1;continue}if(s==="\\"){t+=s,o=!0;continue}if(s==="'"&&!r){n=!n,t+=s;continue}if(s==='"'&&!n){r=!r,t+=s;continue}t+=s===`
9
- `&&!n&&!r?" ; ":s}return t},I=(e)=>{let t,n=dn(we(e));try{t=xt(n,Ht)}catch{return[pe(n)]}t=Tt(t);let r={remaining:Dt(n)},o=[],s=[];for(let u of t){let f=E(u);if(f!==null&&be.has(f)){let g=ge(s,r);if(g!==null)o.push(g);s=[];continue}s.push(u)}let i=ge(s,r);if(i!==null)o.push(i);if(ln(o))o.push(pe(n));for(let u of[...Wt(e),...Ft(e)])for(let f of I(u))o.push(f);let l=o.length;for(let u=0;u<l;u+=1){let f=o[u];for(let g of cn)for(let p of g(f))for(let w of I(p))o.push(w)}return o},un=(e)=>e.startsWith("./")?e.slice(2):e,X=(e,t=[],n=[])=>{if(e==="")return!1;let r=un(e);if(r===".."||r.startsWith("../")||r.includes("/../"))return!1;for(let o of[...ye,...n])if(r===o||r.startsWith(`${o}/`))return!0;for(let o of[...Et,...t])if(r===o||r.startsWith(`${o}/`))return!0;return!1},Ce=(e=[],t=[])=>{let n={"build outputs":["dist","build","_build","out","target"],"js framework outputs":[".next",".nuxt",".svelte-kit",".output",".astro",".angular",".vite",".parcel-cache",".turbo",".vercel",".netlify",".fly",".wrangler",".serverless"],"tests / coverage":["coverage",".nyc_output"],caches:[".cache",".ruff_cache",".mypy_cache",".pytest_cache",".ty_cache",".tox"],"language / package":["__pycache__",".venv","venv","node_modules",".gradle","DerivedData",".bundle",".cargo-target"],"tmp / state":["tmp",".tmp",".state",...ye],iac:[".terraform"],"bundler dev":[".yarn/cache",".yarn/install-state.gz",".pnpm-store",".bun"]};if(e.length>0)n["custom relative"]=e;if(t.length>0)n["custom absolute"]=t;return Object.entries(n).map(([r,o])=>` ${r}: ${o.join(", ")}`).join(`
10
- `)},k=(e)=>/(?<prefix>^|\s)#\s*tripwire-allow:[ \t]*\S[^\r\n]*/.test(we(e));import{readFileSync as fn}from"fs";import{homedir as gn}from"os";import{Cause as hn,Data as He,Effect as R,Schema as c}from"effect";var Pe=c.Struct({pattern:c.String,message:c.String,action:c.optional(c.Union([c.Literal("deny"),c.Literal("ask")])),requiresFlags:c.optional(c.Array(c.String)),forbidsFlagValues:c.optional(c.Array(c.Struct({flag:c.String,values:c.Array(c.String)})))}),mn=c.Struct({protectedBranches:c.optional(c.Array(c.String)),enforceConventionalCommits:c.optional(c.Boolean)}),pn=c.Struct({relative:c.optional(c.Array(c.String)),absolute:c.optional(c.Array(c.String))}),yn=c.Struct({argumentsIncludeAll:c.optional(c.Array(c.String)),argumentsStartWith:c.optional(c.Array(c.String)),shortFlagsIncludeAll:c.optional(c.Array(c.String))}),kn=c.Struct({rule:c.String,executables:c.Array(c.String),action:c.Union([c.Literal("deny"),c.Literal("warn")]),message:c.String,match:c.optional(yn)}),bn=c.Struct({executable:c.String,timeoutMs:c.Finite.check(c.isGreaterThan(0))}),Sn=c.Struct({git:c.optional(mn),safePaths:c.optional(pn),toolPolicies:c.optional(c.Array(kn)),blockedCommands:c.optional(c.Array(Pe)),allowedCommands:c.optional(c.Array(Pe)),secretScanner:c.optional(bn)}),U=`${gn()}/.config/tripwire/config.json`;class Te extends He.TaggedError("ConfigReadError"){}class De extends He.TaggedError("ConfigParseError"){}var wn=(e)=>e instanceof Error&&("code"in e)&&e.code==="ENOENT",Rn=(e)=>R.try({try:()=>fn(e,"utf8"),catch:(t)=>new Te({cause:t})}).pipe(R.catchTag("ConfigReadError",(t)=>wn(t.cause)?R.succeed(null):R.fail(t))),vn=(e)=>R.try({try:()=>JSON.parse(e),catch:(t)=>new De({cause:t})}),xn=(e)=>c.decodeUnknownEffect(Sn)(e,{onExcessProperty:"error"}),B=()=>({git:{protectedBranches:[],enforceConventionalCommits:!1},safePaths:{},toolPolicies:[],blockedCommands:[],allowedCommands:[],secretScanner:{executable:"betterleaks",timeoutMs:5000}}),z=(e)=>{let t=B();return{git:{protectedBranches:e.git?.protectedBranches??t.git.protectedBranches,enforceConventionalCommits:e.git?.enforceConventionalCommits??t.git.enforceConventionalCommits},safePaths:{...t.safePaths,...e.safePaths},toolPolicies:e.toolPolicies??t.toolPolicies,blockedCommands:e.blockedCommands??t.blockedCommands,allowedCommands:e.allowedCommands??t.allowedCommands,secretScanner:e.secretScanner??t.secretScanner}},G=(e=U)=>R.gen(function*(){let n=yield*Rn(e);if(n===null)return{ok:!0,config:B()};let r=yield*vn(n),o=yield*xn(r);return{ok:!0,config:z(o)}}).pipe(R.timeout(1000),R.catchCause((t)=>{let n={ok:!1,error:hn.pretty(t)};return R.succeed(n)})),En=(e=U)=>G(e).pipe(R.flatMap((t)=>t.ok?R.succeed(t.config):R.die(Error(`[tripwire] config load failed (${e}): ${t.error}`))));var Ae=new Set(["preToolUse","beforeShellExecution","beforeMCPExecution","beforeReadFile","beforeTabFileRead"]),ee=new Set(["postToolUse","postToolUseFailure","afterShellExecution","afterMCPExecution","afterFileEdit","afterTabFileEdit"]),te=(e)=>({kind:"cursor",eventName:e,post:ee.has(e)}),_n=(e)=>Ae.has(e)||ee.has(e),T=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),m=(e,...t)=>{for(let n of t){let r=e[n];if(typeof r==="string")return r}return},ne=(e,...t)=>{for(let n of t)if(n in e)return e[n];return},Q=(e,t,...n)=>{let r=m(e,...n);if(r!==void 0)return r;if(typeof t==="string"&&n.includes("command"))return t;return T(t)?m(t,...n):void 0},Ie=(e,t)=>{if(e===void 0)throw Error(`Cursor hook payload missing ${t}`);return e},Cn=(e)=>{let t=e.toLowerCase();if(t==="bash"||t==="exec"||t==="shell"||t==="run_command")return"Bash";if(t==="read"||t==="read_file")return"Read";if(t==="write"||t==="write_file")return"Write";if(t==="edit"||t==="edit_file"||t==="multiedit"||t==="apply_patch")return"Edit";return e},Pn=(e)=>e==="Read"||e==="Write"||e==="Edit",Hn=(e,t)=>{let n;if(typeof e==="string")n=e;else if(T(e))n=m(e,"command","cmd");if(t&&n===void 0)throw Error("Cursor hook payload missing command");if(typeof e==="string")return{command:e};if(!T(e)||n===void 0)return e;return{...e,command:n}},Tn=(e,t,n)=>{let r=m(t,"file_path","filePath","path");if(n&&r===void 0)throw Error(`Cursor ${e} hook payload missing file path`);if(e==="Read")return{...t,file_path:r??""};if(e==="Write"){let o=m(t,"content","fileText","text");if(n&&o===void 0)throw Error("Cursor Write hook payload missing content");return{...t,file_path:r??"",content:o??""}}if(e==="Edit"){let o=m(t,"old_string","oldString"),s=m(t,"new_string","newString");if(n&&(o===void 0||s===void 0))throw Error("Cursor Edit hook payload missing oldString or newString");return{...t,file_path:r??"",old_string:o??"",new_string:s??""}}return t},Y=(e,t,n=!0)=>{if(e==="Bash")return Hn(t,n);if(!Pn(e))return t;if(!T(t)){if(n)throw Error(`Cursor ${e} hook payload is missing tool input`);return t}return Tn(e,t,n)},Fe=(e,t)=>{if(typeof t!=="string")return t;if(e==="Bash")return{stdout:t,stderr:""};if(e==="Read")return{content:t};return{content:t}},Dn=(e,t)=>{if(e==="beforeShellExecution"||e==="afterShellExecution")return"Bash";if(e==="beforeReadFile"||e==="beforeTabFileRead"||e==="afterFileEdit"||e==="afterTabFileEdit")return e.startsWith("before")?"Read":"Edit";let n=m(t,"tool_name","toolName");if(n!==void 0)return Cn(n);let r=ne(t,"tool_input","toolInput");if(m(t,"command","cmd")!==void 0||typeof r==="string"||T(r)&&m(r,"command","cmd")!==void 0)return"Bash";if(m(t,"file_path","filePath","path")!==void 0||T(r)&&m(r,"file_path","filePath","path")!==void 0)return"Read";return""},In=(e,t)=>{if(e==="Bash")return{command:Q(t,void 0,"command","cmd")};let n={file_path:m(t,"file_path","filePath","path")};if(e==="Write")n.content=m(t,"content","fileText","text");if(e==="Edit")n.old_string=m(t,"old_string","oldString"),n.new_string=m(t,"new_string","newString");return n},Fn=(e,t,n,r)=>{let o=ne(n,"tool_input","toolInput"),s=!r;if(e==="beforeShellExecution"||e==="afterShellExecution"){let i=Q(n,o,"command","cmd");return{command:s?Ie(i,"command"):i??""}}if(e==="beforeReadFile"||e==="beforeTabFileRead"||e==="afterFileEdit"||e==="afterTabFileEdit"){let i=Q(n,o,"file_path","filePath","path");return Y(t,{file_path:s?Ie(i,"file path"):i??"",...Array.isArray(n.edits)&&{edits:n.edits}},s)}if(o===void 0&&["Bash","Read","Write","Edit"].includes(t))return Y(t,In(t,n),s);return Y(t,o,s)},$n=(e,t,n)=>{if(e==="afterShellExecution")return Fe(t,m(n,"output")??"");let r=ne(n,"tool_response","toolResponse","tool_output","toolOutput","result","result_json");return Fe(t,r)},$e=(e,t,n)=>{let r=Dn(t,e),o=Fn(t,r,e,n),s=n?$n(t,r,e):void 0,i=m(e,"cwd"),l=m(e,"conversation_id","session_id"),u=m(e,"tool_use_id");return{event:{hook_event_name:n?"PostToolUse":"PreToolUse",...r.length>0&&{tool_name:r},...o!==void 0&&{tool_input:o},...s!==void 0&&{tool_response:s},...i!==void 0&&{cwd:i},...l!==void 0&&{session_id:l},...u!==void 0&&{tool_use_id:u}},host:te(t)}},We=(e,t)=>{if(t!==void 0&&!_n(t))throw Error(`Unknown Cursor hook event "${t}"`);if(!T(e)){if(t!==void 0)throw Error(`Cursor ${t} hook payload must be a JSON object`);return{event:e,host:{kind:"native"}}}let n=t??m(e,"hook_event_name");if(n!==void 0&&Ae.has(n))return $e(e,n,!1);if(n!==void 0&&ee.has(n))return $e(e,n,!0);return{event:e,host:{kind:"native"}}};import{Schema as b}from"effect";var re=b.Struct({hook_event_name:b.String,tool_name:b.optional(b.String),tool_input:b.optional(b.Unknown),tool_response:b.optional(b.Unknown),cwd:b.optional(b.String),session_id:b.optional(b.String),turn_id:b.optional(b.String),tool_use_id:b.optional(b.String)}),F=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),Be=(e)=>F(e)&&typeof e.command==="string",Oe=(e)=>F(e)&&typeof e.file_path==="string"&&typeof e.old_string==="string"&&typeof e.new_string==="string",Le=(e)=>F(e)&&typeof e.file_path==="string"&&typeof e.content==="string",Me=(e)=>F(e)&&typeof e.file_path==="string",Ne=(e,t)=>{if(!F(t))return"";if(e==="Bash"){let n=typeof t.stdout==="string"?t.stdout:"",r=typeof t.stderr==="string"?t.stderr:"";return[n,r].filter((o)=>o.length>0).join(`
11
- `)}if(e==="Read"){if(typeof t.content==="string")return t.content;let{file:n}=t;return F(n)&&typeof n.content==="string"?n.content:""}return typeof t.content==="string"?t.content:""};import{appendFileSync as An,mkdirSync as Wn}from"fs";import{homedir as Bn}from"os";import On from"path";var je=`${Bn()}/.claude/tripwire.log`;try{Wn(On.dirname(je),{recursive:!0})}catch{}var v=(e,t)=>{let n=new Date().toISOString(),r=t instanceof Error?t.stack??t.message:String(t);try{An(je,`[${n}] [${e}] ${r}
12
- `)}catch{}};var Ln=(e)=>e.tokens.slice(1).join(" "),Ue=(e,...t)=>{let n=new Set(e.flags);return t.some((r)=>n.has(r))},Mn=[{rule:"unsupported-shell-structure",action:"deny",message:"Tripwire cannot inspect every executable branch in this shell structure. Rewrite it as simple commands joined with `;`, `&&`, or `||` so each command can be checked.",match:(e)=>e.head===N},{rule:"rm-rf-root",action:"deny",message:"rm -rf / is catastrophic. If the goal is cleaning a subdirectory, scope the path inside the project (e.g. ./dist).",match:(e)=>e.head==="rm"&&Ue(e,"-rf","-fr","-Rf","-fR")&&e.tokens.includes("/")},{rule:"rm-rf-home",action:"deny",message:"rm -rf on $HOME / ~ would erase the home directory. Refuse.",match:(e)=>e.head==="rm"&&Ue(e,"-rf","-fr","-Rf","-fR")&&e.tokens.some((t)=>/^(?<home>~|\$HOME|\$\{HOME\})$/.test(t))},{rule:"fork-bomb",action:"deny",message:"Fork bomb pattern detected. Refuse.",match:(e,t)=>/:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/.test(t)},{rule:"source-script",action:"deny",message:"`source` / `.` executes a file in the current shell. Refuse arbitrary sourced scripts.",match:(e)=>(e.head==="source"||e.head===".")&&e.tokens.length>1},{rule:"dd-raw-device",action:"deny",message:"dd writing to a raw block device wipes the disk. Refuse.",match:(e)=>e.head==="dd"&&/\bof=\/dev\/(?<type>disk|sd|nvme|rdisk)/i.test(Ln(e))},{rule:"mkfs",action:"deny",message:"mkfs formats a filesystem. Refuse.",match:(e)=>/^mkfs(?<ext>\.[a-z0-9]+)?$/i.test(e.head)},{rule:"kill-all",action:"deny",message:"kill -9 -1 kills every process you own. Refuse.",match:(e)=>e.head==="kill"&&e.tokens.includes("-9")&&e.tokens.includes("-1")},{rule:"chmod-777-recursive",action:"deny",message:"Recursive chmod 777 makes everything world-writable. Use 755 for directories, 644 for files, scoped narrowly.",match:(e)=>e.head==="chmod"&&e.flags.some((t)=>t.includes("R"))&&e.tokens.includes("777")},{rule:"no-verify",action:"deny",message:"--no-verify skips git hooks. Per policy: never skip hooks. Fix the underlying issue.",match:(e)=>e.tokens.includes("--no-verify")},{rule:"no-gpg-sign",action:"deny",message:"Bypassing GPG signing is off-limits unless explicitly requested.",match:(e,t)=>/--no-gpg-sign\b|-c\s+commit\.gpgsign=false/.test(t)},{rule:"sudo",action:"ask",message:"sudo escalates privileges and is almost never needed in a coding session. If genuinely required, explain why; otherwise find a non-sudo path.",match:(e)=>e.head==="sudo"},{rule:"shutdown",action:"deny",message:"shutdown / reboot / halt control the machine. Refuse \u2014 system control should be done manually.",match:(e)=>["shutdown","reboot","halt","poweroff"].includes(e.head)},{rule:"launchctl-mutation",action:"deny",message:"launchctl load/unload/bootstrap/bootout/kickstart mutates system services. Refuse \u2014 surface the intent instead.",match:(e)=>e.head==="launchctl"&&typeof e.tokens[1]==="string"&&["load","unload","bootstrap","bootout","kickstart","enable","disable"].includes(e.tokens[1])},{rule:"defaults-write",action:"deny",message:"`defaults write` mutates macOS preferences. Refuse \u2014 surface the intent.",match:(e)=>e.head==="defaults"&&e.tokens[1]==="write"},{rule:"csrutil",action:"deny",message:"csrutil controls System Integrity Protection. Refuse.",match:(e)=>e.head==="csrutil"},{rule:"nvram",action:"deny",message:"nvram modifies firmware variables. Refuse.",match:(e)=>e.head==="nvram"},{rule:"diskutil-destructive",action:"deny",message:"diskutil eraseDisk / reformat / partitionDisk wipes disks. Refuse.",match:(e)=>e.head==="diskutil"&&typeof e.tokens[1]==="string"&&["eraseDisk","reformat","partitionDisk","eraseVolume","secureErase"].includes(e.tokens[1])},{rule:"tmutil-destructive",action:"deny",message:"tmutil delete / disablelocal touches Time Machine. Refuse.",match:(e)=>e.head==="tmutil"&&typeof e.tokens[1]==="string"&&["delete","disablelocal"].includes(e.tokens[1])},{rule:"osascript",action:"ask",message:"osascript runs arbitrary AppleScript and can do almost anything (move files, send emails, drive apps). Confirm with the user what you want done before running it.",match:(e)=>e.head==="osascript"},{rule:"topgrade",action:"deny",message:"topgrade upgrades everything (brew, mas, npm globals, rust, mise). System upgrades should be done manually.",match:(e)=>e.head==="topgrade"},{rule:"rsync-delete",action:"deny",message:"rsync --delete removes files at the destination. High blast radius \u2014 surface the intent instead.",match:(e)=>e.head==="rsync"&&e.flags.includes("--delete")},{rule:"softwareupdate-install",action:"deny",message:"`softwareupdate --install / -i / -d` triggers macOS system updates. Refuse \u2014 system updates should be done manually.",match:(e)=>e.head==="softwareupdate"&&e.flags.some((t)=>t==="--install"||t==="-i"||t==="-d"||t.startsWith("--download"))},{rule:"pmset-write",action:"deny",message:"`pmset` (with arguments) writes power-management settings. Read-only `pmset -g` is fine; mutations need the user.",match:(e)=>e.head==="pmset"&&e.tokens.length>1&&!e.tokens.includes("-g")&&!e.tokens.includes("-G")},{rule:"dscl-mutate",action:"deny",message:"`dscl . -create / -delete / -append / -change / -merge` modifies the local directory service (your user account, groups, etc.). Refuse.",match:(e)=>e.head==="dscl"&&e.tokens.some((t)=>["-create","-delete","-append","-change","-merge","-passwd"].includes(t))},{rule:"xattr-quarantine-bypass",action:"deny",message:"`xattr -d com.apple.quarantine` removes Gatekeeper's quarantine bit \u2014 the macOS protection against running untrusted binaries. Refuse.",match:(e)=>e.head==="xattr"&&e.tokens.includes("-d")&&e.tokens.some((t)=>t.includes("com.apple.quarantine"))},{rule:"spctl-disable",action:"deny",message:"`spctl --master-disable` / `--global-disable` disables Gatekeeper system-wide. Refuse.",match:(e)=>e.head==="spctl"&&e.flags.some((t)=>t==="--master-disable"||t==="--global-disable"||t==="--disable")},{rule:"kextload",action:"deny",message:"Loading a kernel extension (`kextload`, `kmutil load`) is a system-level mutation. Refuse \u2014 the user handles this manually.",match:(e)=>e.head==="kextload"||e.head==="kextunload"||e.head==="kmutil"&&(e.tokens[1]==="load"||e.tokens[1]==="unload")},{rule:"security-keychain-destructive",action:"deny",message:"`security delete-keychain / delete-generic-password / delete-internet-password / set-keychain-settings` mutates your Keychain (where CLIs store their secrets). Refuse \u2014 Keychain should be managed manually.",match:(e)=>e.head==="security"&&typeof e.tokens[1]==="string"&&["delete-keychain","delete-generic-password","delete-internet-password","delete-certificate","delete-identity","set-keychain-settings","unlock-keychain","lock-keychain","create-keychain"].includes(e.tokens[1])},{rule:"security-keychain-add-write",action:"ask",message:"`security add-generic-password / add-internet-password / add-certificate` writes to your Keychain. Other tools manage their own entries \u2014 confirm this is the right path before adding anything else manually.",match:(e)=>e.head==="security"&&typeof e.tokens[1]==="string"&&["add-generic-password","add-internet-password","add-certificate"].includes(e.tokens[1])},{rule:"systemsetup",action:"deny",message:"`systemsetup -set...` writes machine-wide settings (timezone, sleep, network time, restart-on-power-failure). Refuse.",match:(e)=>e.head==="systemsetup"&&e.tokens.some((t)=>t.startsWith("-set"))},{rule:"scutil-set",action:"deny",message:"`scutil --set` writes system configuration (computer name, hostname, LocalHostName). Refuse \u2014 read-only `scutil --get` is fine.",match:(e)=>e.head==="scutil"&&e.tokens.includes("--set")},{rule:"brew-mutation",action:"ask",message:"`brew install/uninstall/upgrade/untap` modifies the machine globally. Confirm before running.",match:(e)=>e.head==="brew"&&typeof e.tokens[1]==="string"&&["install","uninstall","upgrade","reinstall","untap","tap"].includes(e.tokens[1])},{rule:"mas-mutation",action:"ask",message:"`mas install/uninstall` changes installed Mac App Store apps. Confirm.",match:(e)=>e.head==="mas"&&typeof e.tokens[1]==="string"&&["install","uninstall","purchase"].includes(e.tokens[1])},{rule:"gh-destructive",action:"deny",message:"gh repo/release/issue delete is destructive on shared state. Refuse.",match:(e)=>e.head==="gh"&&typeof e.tokens[1]==="string"&&typeof e.tokens[2]==="string"&&["repo","release","issue","pr"].includes(e.tokens[1])&&["delete","destroy","remove"].includes(e.tokens[2])},{rule:"flyctl-destroy",action:"deny",message:"flyctl apps destroy / volumes destroy nukes deployed infra. Refuse.",match:(e)=>e.head==="flyctl"&&e.tokens.some((t)=>t==="destroy"||t==="delete")},{rule:"gcloud-delete",action:"deny",message:"`gcloud ... delete` affects cloud resources. Refuse.",match:(e)=>e.head==="gcloud"&&e.tokens.includes("delete")}],Nn=new Set(["unsupported-shell-structure","rm-rf-root","rm-rf-home","fork-bomb","dd-raw-device","mkfs","kill-all","diskutil-destructive","tmutil-destructive","shutdown","csrutil","nvram","kextload","spctl-disable","xattr-quarantine-bypass","topgrade","softwareupdate-install","systemsetup","scutil-set","security-keychain-destructive","no-verify","no-gpg-sign"]),ze=(e,t)=>{let n=k(t),r=[];for(let o of e)for(let s of Mn){if(!s.match(o,t))continue;if(n&&!Nn.has(s.rule))continue;r.push(s.action==="deny"?d(s.rule,s.message):y(s.rule,s.message));break}return r.length===0?a("bash-deny"):D(r)};var oe=(e)=>e.protectedBranches??[],jn=/^(?<type>feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(?<scope>\([\w./\- ]+\))?!?:\s+\S/,Un=new Set(["-C","-c","--git-dir","--work-tree","--namespace","--super-prefix"]),zn=new Set(["--bare","--paginate","-p","--no-pager","--no-replace-objects","--literal-pathspecs","--glob-pathspecs","--noglob-pathspecs","--icase-pathspecs","--no-optional-locks","--exec-path","--html-path","--man-path","--info-path"]),Gn=(e)=>{if(e.head!=="git")return null;let t=e.tokens.slice(1),n=0;while(n<t.length){let r=t[n];if(r===void 0)break;if(Un.has(r)){n+=2;continue}if(zn.has(r)){n+=1;continue}if(r.startsWith("--git-dir=")||r.startsWith("--work-tree=")||r.startsWith("--namespace=")||r.startsWith("--super-prefix=")||r.startsWith("--exec-path=")){n+=1;continue}if(r.startsWith("-")){n+=1;continue}return{subcommand:r,subArgs:t.slice(n+1)}}return null},Jn=(e,t)=>{for(let n=0;n<e.length;n+=1){let r=e[n];if(r===void 0)break;if(r==="-m"||r==="--message"){let o=e[n+1];return o===void 0?null:j(o,t)}if(r.startsWith("--message="))return j(r.slice(10),t);if(/^-[a-zA-Z]*m[a-zA-Z]*$/.test(r)){let o=e[n+1];return o===void 0?null:j(o,t)}}return null},qn=(e,t)=>{let n=oe(t);for(let r of e)for(let o of n)if(r===o||r.endsWith(`:${o}`)||r.endsWith(`/${o}`))return o;return null},Vn=(e)=>e.filter((t)=>!t.startsWith("-")),Kn=(e)=>e.filter((t)=>t.startsWith("-")),h=(e,...t)=>{let n=new Set(e);return t.some((r)=>n.has(r))},Zn=({subArgs:e,positional:t})=>{if(h(e,"--global","--system"))return d("git-config-global","Modifying global / system git config is off-limits \u2014 that is your personal identity. Read-only `git config --get` is fine.");if(!h(e,"--get","-l","--list","--get-all","--get-regexp")&&t.length>=2)return d("git-config-write","Local git config writes should be done explicitly. To read a value, use `git config --get <key>`.");return a("bash-git")},Xn=({subArgs:e})=>{if(h(e,"--cached"))return a("bash-git");return y("git-rm","`git rm <path>` removes from the index AND the working tree. To untrack-only, use `git rm --cached <path>`. To delete the file separately, use `trash` / `rip`. Confirm intent.")},Yn=({subArgs:e,positional:t})=>{if(h(e,"--staged","-S")&&!h(e,"--worktree","-W"))return a("bash-git");if(t.length>0)return d("git-restore-discard","`git restore <path>` discards uncommitted changes in the working tree. Refuse \u2014 `git diff <path>` to inspect first, or `git stash push <path>` to preserve.");return a("bash-git")},Qn=({subArgs:e,positional:t})=>{if(h(e,"-b","-B"))return a("bash-git");if(h(e,"-f","--force"))return d("git-checkout-force","`git checkout -f` overwrites the working tree without preserving uncommitted changes. Refuse.");if(e.includes("--"))return d("git-checkout-discard","`git checkout -- <path>` discards uncommitted working-tree changes. Refuse \u2014 use `git stash push <path>` to preserve, or `git diff <path>` to inspect first.");let[n]=t;if(n!==void 0&&t.length===1&&(n==="."||n.startsWith("./")))return d("git-checkout-discard-all","`git checkout .` discards ALL uncommitted working-tree changes. Refuse \u2014 `git stash` to preserve, or `git diff` to inspect first.");return a("bash-git")},er=({subArgs:e})=>{if(h(e,"-f","--force","--discard-changes"))return d("git-switch-force","`git switch -f / --discard-changes` throws away uncommitted working-tree changes. Refuse.");return a("bash-git")},tr=({subArgs:e,flags:t,positional:n})=>{if(h(e,"--hard"))return d("git-reset-hard","`git reset --hard` discards all uncommitted changes AND moves HEAD. Refuse \u2014 describe the intent in chat. If undoing a published commit, `git revert <sha>` is safer.");if(h(e,"--keep"))return y("git-reset-keep","`git reset --keep` resets HEAD but preserves uncommitted local changes. Confirm intent.");if(n.length===0&&t.length===0)return a("bash-git");return y("git-reset-mixed","`git reset` moves HEAD. Confirm intent \u2014 if undoing a commit, `git revert` is usually safer.")},nr=({flags:e})=>{if(e.some((t)=>/^-[a-zA-Z]*[df]/.test(t)||t==="--force"))return d("git-clean-fd","`git clean -fd` deletes untracked files (often your in-progress work). Refuse \u2014 inspect with `git clean -dn` (dry run) first. If genuinely needed, append ` # tripwire-allow: <reason>`.");return a("bash-git")},rr=({subArgs:e,positional:t,config:n})=>{if(h(e,"--abort","--quit","--continue","--skip","--edit-todo"))return a("bash-git");if(h(e,"-i","--interactive"))return d("git-rebase-interactive","`git rebase -i` rewrites history interactively. Refuse \u2014 too easy to lose commits in the agent loop. If this is genuinely required, do it manually outside the agent.");let[r]=t,o=oe(n);if(r!==void 0&&o.includes(r))return y("git-rebase-onto-protected",`Rebasing onto \`${r}\` rewrites history of the current branch. \`git merge ${r}\` is usually safer. Confirm intent.`);return y("git-rebase","`git rebase` rewrites commit history. `git merge` is usually safer. Confirm intent.")},or=({subArgs:e})=>{if(h(e,"--abort","--quit","--continue","--skip"))return a("bash-git");return y("git-cherry-pick","`git cherry-pick` applies commits onto the current branch and can create conflicts. Confirm intent.")},sr=({subArgs:e})=>{if(h(e,"--abort","--continue","--quit"))return a("bash-git");return y("git-merge","`git merge <branch>` may create merge conflicts. Confirm intent.")},ir=({subArgs:e,config:t,heredocBodies:n})=>{if(h(e,"--amend"))return d("git-commit-amend","`git commit --amend` rewrites the last commit. If it has been pushed, this causes upstream divergence. Refuse \u2014 surface the intent.");let r=Jn(e,n),o=h(e,"-F","--file","-c","-C","--reuse-message","--reedit-message"),s=h(e,"--no-edit");if(r===null&&!o&&!s)return d("git-commit-no-message",'`git commit` without `-m "..."` opens an editor and hangs the agent. Use `git commit -m "<conventional message>"`.');if(r!==null&&t.enforceConventionalCommits===!0&&!jn.test(r))return d("git-commit-non-conventional",["Commit message must follow Conventional Commits format:"," `<type>(<scope>)?(!)?: <description>`","Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.","Examples:"," `fix(auth): handle expired token refresh`"," `feat: add bash-git rule`"," `chore: bump deps`",`Got: ${JSON.stringify(r)}`].join(`
13
- `));if(h(e,"-a","--all")||e.some((i)=>/^-[a-zA-Z]*a[a-zA-Z]*$/.test(i)))return y("git-commit-auto-stage","`git commit -a / --all` auto-stages every tracked change. Explicit `git add <files>` first is usually clearer about what is being committed. Confirm.");return a("bash-git")},ar=({subArgs:e,flags:t,positional:n,config:r})=>{if(t.some((s)=>s==="--force"||s==="-f"||s.startsWith("--force-with-lease")))return d("git-force-push","Force push is forbidden. If a branch needs to be reset upstream, surface the intent \u2014 there is almost always a non-force path.");if(h(e,"--delete","--mirror")||e.some((s)=>s.startsWith(":")))return d("git-push-delete","Refusing to delete a remote branch via push. If genuinely needed, surface the intent.");let o=qn(n,r);if(o!==null)return d("git-push-protected",`Refusing to push directly to protected branch \`${o}\`. Open a PR instead: \`gh pr create\`.`);return a("bash-git")},cr=({subArgs:e,flags:t,positional:n,config:r})=>{let o=t.find((s)=>s==="-D"||s==="-d"||s==="--delete"||/^-[a-zA-Z]*D/.test(s)||/^-[a-zA-Z]*d/.test(s));if(o!==void 0){let s=n,i=new Set(oe(r)),l=s.find((u)=>i.has(u));if(l!==void 0)return d("git-branch-delete-protected",`Refusing to delete protected branch \`${l}\`.`);if(o==="-D"||o.includes("D"))return d("git-branch-force-delete",`\`git branch -D ${s.join(" ")}\` force-deletes branches even if unmerged (potential data loss). To delete a merged branch, use \`-d\`. To force, append \` # tripwire-allow: <reason>\`.`);return y("git-branch-delete",`\`git branch -d ${s.join(" ")}\` deletes a branch (merged-only check). Confirm intent.`)}if(h(e,"-m","-M","--move"))return y("git-branch-move","Renaming a branch can confuse pushed remotes. Confirm intent.");return a("bash-git")},lr=({subArgs:e})=>{if(h(e,"-d","--delete"))return d("git-tag-delete","`git tag -d` deletes a tag. Refuse \u2014 surface intent.");return a("bash-git")},dr=({subArgs:e})=>{let t=e[0]??"push";if(t==="drop"||t==="clear")return d("git-stash-drop",`\`git stash ${t}\` discards stashed work. Refuse \u2014 \`git stash list\` and \`git stash show\` to inspect first.`);return a("bash-git")},ur=({flags:e})=>{if(e.some((t)=>t.startsWith("--prune=")||t==="--aggressive"))return d("git-gc-prune","`git gc --prune=now` / `--aggressive` destroys reflog recovery options. Refuse.");return a("bash-git")},fr=({subArgs:e})=>{let[t]=e;if(t==="add"||t==="remove"||t==="rm"||t==="set-url"||t==="rename")return y("git-remote-mutate",`\`git remote ${t}\` changes which remote you're pushing to. Confirm \u2014 accidentally pointing at the wrong remote is high blast-radius.`);return a("bash-git")},Ge=({subcommand:e,subArgs:t})=>{let n=t[0]??"";if(["add","remove","rm","deinit","sync","set-url"].includes(n))return y("git-submodule-worktree-mutate",`\`git ${e} ${n}\` modifies repo structure. Confirm intent.`);return a("bash-git")},gr=new Map([["config",Zn],["add",()=>a("bash-git")],["mv",()=>a("bash-git")],["rm",Xn],["restore",Yn],["checkout",Qn],["switch",er],["reset",tr],["clean",nr],["rebase",rr],["cherry-pick",or],["merge",sr],["commit",ir],["push",ar],["branch",cr],["tag",lr],["stash",dr],["filter-branch",({subcommand:e})=>d("git-filter",`\`git ${e}\` rewrites entire repo history. Refuse.`)],["filter-repo",({subcommand:e})=>d("git-filter",`\`git ${e}\` rewrites entire repo history. Refuse.`)],["gc",ur],["update-ref",()=>d("git-update-ref","`git update-ref` directly mutates refs and bypasses normal git operations. Refuse.")],["remote",fr],["submodule",Ge],["worktree",Ge],["init",({subcommand:e})=>x(`git-${e}`,`\`git ${e}\` is allowed but unusual mid-session. Make sure this is what the user asked for.`)],["clone",({subcommand:e})=>x(`git-${e}`,`\`git ${e}\` is allowed but unusual mid-session. Make sure this is what the user asked for.`)]]),hr=(e,t,n)=>{let{subcommand:r,subArgs:o}=e,s=Kn(o),i=Vn(o);if(new Set(["status","diff","log","show","blame","rev-parse","rev-list","ls-files","ls-tree","cat-file","reflog","describe","shortlog","whatchanged","archive","bundle","fsck","fetch","ls-remote","help","version","grep","name-rev","merge-base","symbolic-ref","check-ignore","count-objects","verify-commit","verify-tag"]).has(r)){if(r==="reflog"&&(o[0]==="expire"||h(o,"--expire")))return d("git-reflog-expire","`git reflog expire` destroys git's recovery history. Refuse \u2014 surface the intent.");if(r==="symbolic-ref"&&i.length>=2)return d("git-symbolic-ref-write","`git symbolic-ref <name> <ref>` rewrites a symbolic ref. Refuse.");return a("bash-git")}let u=gr.get(r);if(u!==void 0)return u({subcommand:r,subArgs:o,flags:s,positional:i,config:t,heredocBodies:n});return x("git-unknown-subcommand",`\`git ${r}\` is not classified by tripwire. Allowing \u2014 flag if this looks like history-rewriting or data-loss territory.`)},Je=(e,t,n)=>{if(k(t))return a("bash-git");let r=Re(t);for(let o of e){let s=Gn(o);if(s===null)continue;let i=hr(s,n,r);if(i!==null&&i.kind!=="allow")return i}return a("bash-git")};var mr=new Set(["curl","wget","wget2","aria2c","xh"]),pr=new Set(["bash","sh","zsh","fish"]),yr=(e)=>{for(let t=0;t<e.length-1;t+=1){let n=e[t],r=e[t+1];if(n===void 0||r===void 0)continue;if(mr.has(n.head)&&pr.has(r.head))return!0}return!1},kr=[{head:"cargo",subcommand:"install",rule:"cargo-install",message:"Confirm before `cargo install <crate>`: this builds and installs arbitrary code from crates.io into ~/.cargo/bin globally."},{head:"go",subcommand:"install",rule:"go-install",message:"Confirm before `go install`: this fetches and installs arbitrary Go code globally."},{head:"gem",subcommand:"install",rule:"gem-install",message:"Confirm before `gem install`: pulls arbitrary code from rubygems.org."}],qe=(e,t)=>{if(k(t))return a("bash-network-install");if(yr(e))return d("curl-pipe-shell","Piping `curl` / `wget` directly into a shell runs whatever the remote URL serves. Refuse \u2014 download to a file, inspect, then run if appropriate. If you genuinely need this, append ` # tripwire-allow: <reason>` (and explain to the user what you're running).");for(let n of e)for(let r of kr)if(n.head===r.head&&n.tokens[1]===r.subcommand)return y(r.rule,r.message);return a("bash-network-install")};import{lstatSync as br,readlinkSync as Sr,realpathSync as wr}from"fs";import A from"path";var Rr=[{pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/,rule:"env-file",message:".env files hold secrets that should never be sent to the model. Refuse to write or edit. If an example is needed, create .env.example with redacted placeholders."},{pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/,rule:"dev-vars",message:".dev.vars holds Cloudflare/Wrangler secrets. Do not modify."},{pattern:/(?<prefix>^|\/)\.ssh\//,rule:"ssh-dir",message:"Never write into ~/.ssh/. Refuse."},{pattern:/(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)(?<pub>\.pub)?$/,rule:"ssh-key",message:"SSH key file. Refuse."},{pattern:/\.(?<ext>pem|key|p12|pfx)$/i,rule:"private-key",message:"Private key file. Refuse to overwrite. If generating a new key, use a different filename and let the user review."},{pattern:/(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/i,rule:"secrets-file",message:"Secrets file. Refuse."},{pattern:/(?<prefix>^|\/)\.aws\/credentials$/,rule:"aws-credentials",message:"AWS credentials file. Refuse."},{pattern:/(?<prefix>^|\/)\.netrc$/,rule:"netrc",message:".netrc holds host credentials. Refuse."}],Ve=(e)=>{try{return wr(e)}catch{return null}},se=(e,t=new Set)=>{if(t.has(e))return e;t.add(e);let n=Ve(e);if(n!==null)return n;try{if(br(e).isSymbolicLink()){let o=Sr(e);return se(A.resolve(A.dirname(e),o),t)}}catch{}let r=A.dirname(e);if(r===e)return e;return A.join(se(r,t),A.basename(e))},O=(e,t,n)=>{let r=A.resolve(e),o=t==="write"?se(r):Ve(r),s=[e,r];if(o!==null&&!s.includes(o))s.push(o);for(let i of s)for(let l of n)if(l.pattern.test(i))return l;return null},ie=(e)=>{let t=O(e.file_path,"write",Rr);if(t!==null)return d(t.rule,t.message);return a("path-protect")};var vr=[{rule:"redirect-env",pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/,message:"Refusing to write into a .env file via shell redirect / tee / cp / mv. .env files hold secrets \u2014 never overwrite from a tool call."},{rule:"redirect-dev-vars",pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/,message:"Refusing to write into .dev.vars (Cloudflare/Wrangler secrets)."},{rule:"redirect-ssh",pattern:/(?<prefix>^|\/)\.ssh\//,message:"Refusing to write into ~/.ssh/ via shell."},{rule:"redirect-key",pattern:/\.(?<ext>pem|key|p12|pfx)$/i,message:"Refusing to overwrite a private-key-shaped file via shell."},{rule:"redirect-aws-credentials",pattern:/(?<prefix>^|\/)\.aws\/credentials$/,message:"Refusing to write into ~/.aws/credentials via shell."},{rule:"redirect-netrc",pattern:/(?<prefix>^|\/)\.netrc$/,message:"Refusing to write into ~/.netrc via shell."},{rule:"redirect-block-device",pattern:/^\/dev\/(?<type>sd|disk|nvme|rdisk)/i,message:"Redirecting into a raw block device wipes the disk. Refuse."}],ae=(e)=>{let t=O(e,"write",vr);if(t!==null)return d(t.rule,t.message);return null},Ke=(e,t)=>{if(k(t))return a("bash-redirect");for(let n of e){for(let r of n.redirects)if(r.op===">"||r.op===">>"){let o=ae(r.target);if(o!==null)return o}if(n.head==="tee")for(let r of n.args){let o=ae(r);if(o!==null)return o}if(n.head==="cp"||n.head==="mv"){let r=n.args.at(-1);if(r!==void 0){let o=ae(r);if(o!==null)return o}}}return a("bash-redirect")};var xr=(e,t)=>{let n=!1,r=[];for(let i of e.tokens.slice(1)){if(!n&&i==="--"){n=!0;continue}if(!n&&i.startsWith("-")&&i!=="-")continue;r.push(i)}let o=t.relative??[],s=t.absolute??[];return r.filter((i)=>!X(i,o,s))},Er=(e,t)=>{if(!e.tokens.includes("-delete"))return null;let n=[];for(let i of e.tokens.slice(1)){if(i.startsWith("-"))break;n.push(i)}let r=n.length===0?["."]:n,o=t.relative??[],s=t.absolute??[];return r.filter((i)=>!X(i,o,s))},Ze=(e,t,n)=>{if(k(t))return a("bash-scoped-rm");let r=[];for(let l of e){if(l.head==="rm"){let u=xr(l,n);if(u.length>0)r.push({kind:"rm",targets:u});continue}if(l.head==="find"){let u=Er(l,n);if(u!==null&&u.length>0)r.push({kind:"find -delete",targets:u})}}if(r.length===0)return a("bash-scoped-rm");let o=n.relative??[],s=n.absolute??[],i=r.map((l)=>` \u2022 ${l.kind} on: ${l.targets.map((u)=>JSON.stringify(u)).join(", ")}`).join(`
14
- `);return d("destructive-outside-safe-paths",`Destructive deletion outside known-safe scopes is blocked. Use a recoverable deletion tool or limit the target to an ephemeral build, cache, state, or temporary directory:
15
- ${Ce(o,s)}
2
+ var pt={allow:0,warn:1,ask:2,deny:3},u=(e)=>({kind:"allow",rule:e,message:""}),T=(e,t)=>({kind:"warn",rule:e,message:t}),b=(e,t)=>({kind:"ask",rule:e,message:t}),f=(e,t)=>({kind:"deny",rule:e,message:t}),V=(e)=>{let t=u("none");for(let n of e)if(pt[n.kind]>pt[t.kind])t=n;return t};import{BunRuntime as Yl}from"@effect/platform-bun";import{Cause as he,Data as Yn,Effect as P,Exit as Ce,Option as Gn,Schema as Re}from"effect";var K="__tripwire_dynamic_shell_value__";var _e=(e)=>({start:e.pos,end:e.end}),cr=(e)=>{let t=e.lastIndexOf("/");return t===-1?e:e.slice(t+1)},De=()=>({aliases:new Map,backgroundPidAvailable:!1,bindings:new Map,functions:new Map,temps:new Map}),v=(e)=>({aliases:new Map(e.aliases),backgroundPidAvailable:e.backgroundPidAvailable,bindings:new Map(e.bindings),functions:new Map(e.functions),temps:new Map(e.temps)}),Te=(e)=>e.type==="SingleQuoted"||e.type==="DoubleQuoted"||e.type==="AnsiCQuoted"||e.type==="LocaleString",mt=(e)=>e.every((t)=>{switch(t.type){case"Literal":case"SingleQuoted":case"AnsiCQuoted":return!0;case"DoubleQuoted":case"LocaleString":return mt(t.parts);case"SimpleExpansion":case"ParameterExpansion":case"CommandExpansion":case"ArithmeticExpansion":case"ProcessSubstitution":case"ExtendedGlob":case"BraceExpansion":return!1;default:return!1}}),L=(e)=>mt(e.parts??[]),gt=(e)=>{if(e===void 0||(e.errors?.length??0)>0||e.commands.length!==1)return null;let[t]=e.commands;if(t===void 0||t.background===!0||t.redirects.length>0)return null;let{command:n}=t;if(n.type!=="Command"||n.name===void 0||!L(n.name))return null;if(n.prefix.length>0||n.suffix.some((r)=>!L(r)))return null;return{command:n,head:cr(n.name.value),args:n.suffix.map((r)=>r.value)}},yt=(e)=>{let t=e.parts??[];if(t.length!==1)return;let[n]=t;if(n?.type==="CommandExpansion")return n.script;if(n?.type!=="DoubleQuoted"||n.parts.length!==1)return;let[r]=n.parts;return r?.type==="CommandExpansion"?r.script:void 0},bt=(e,t)=>e.command.name?.value.includes("/")===!1&&(t.aliases.has(e.head)||t.functions.has(e.head)),We=(e,t)=>{let n=gt(yt(e));if(n===null||bt(n,t))return null;if(n.head==="cat"&&n.args.length===0&&n.command.redirects.length===1){let[r]=n.command.redirects;if(r?.operator!=="<<"&&r?.operator!=="<<-")return null;if(r.heredocQuoted===!0||r.body===void 0||L(r.body))return(r.content??"").replace(/\n+$/u,"");return null}if(n.head==="printf"&&n.args.length===2){let[r,s]=n.args;if(r==="%s"||r===String.raw`%s\n`)return s??""}return null},ht=(e)=>["/tmp","/private/tmp","/var/tmp","/private/var/tmp","/var/folders","/private/var/folders"].some((t)=>e===t||e.startsWith(`${t}/`)),dr=(e)=>{let t=!1,n=null,r=[];for(let s=0;s<e.length;s+=1){let i=e[s];if(i==="-t")t=!0,s+=1;else if(i==="-p"||i==="--tmpdir")n=e[s+1]??"",s+=1;else if(i?.startsWith("--tmpdir=")===!0)n=i.slice(9);else if(i!==void 0&&!i.startsWith("-"))r.push(i)}return{explicitDirectory:n,positionals:r,systemTempTemplate:t}},St=(e,t)=>{let n=gt(yt(e));if(n?.head!=="mktemp"||bt(n,t)||["TMPDIR","TMP","TEMP"].some((a)=>t.bindings.has(a)||t.temps.has(a))||!n.args.some((a)=>a==="-d"||a==="--directory"||/^-[^-]*d/u.test(a)))return!1;let{explicitDirectory:r,positionals:s,systemTempTemplate:i}=dr(n.args);if(r!==null&&!ht(r))return!1;if(s.length===0||i||r!==null)return!0;let[o]=s;return s.length===1&&o!==void 0&&ht(o)},kt=(e)=>{if(e.type==="SimpleExpansion")return/^\$(?<name>[A-Za-z_][A-Za-z0-9_]*|[0-9]+|!)$/u.exec(e.text)?.groups?.name??null;if(e.type==="ParameterExpansion"&&e.indirect!==!0&&e.length!==!0&&e.operator===void 0&&e.index===void 0&&e.slice===void 0&&e.replace===void 0&&/^(?:[A-Za-z_][A-Za-z0-9_]*|[0-9]+|!)$/u.test(e.parameter))return e.parameter;return null},vt=(e)=>{let t=[],n=(o,a)=>{for(let l of o)switch(l.type){case"Literal":case"SingleQuoted":case"AnsiCQuoted":{t.push({kind:"literal",value:l.value});break}case"DoubleQuoted":case"LocaleString":{if(!n(l.parts,!0))return!1;break}case"SimpleExpansion":case"ParameterExpansion":{let c=kt(l);if(c===null)return!1;t.push({kind:"variable",quoted:a,variable:c});break}case"ArithmeticExpansion":case"BraceExpansion":case"CommandExpansion":case"ExtendedGlob":case"ProcessSubstitution":return!1;default:return!1}return!0};if(!n(e.parts??[],!1))return null;let r=t.filter((o)=>o.kind==="variable");if(r.length!==1)return null;let[s]=r;if(s===void 0)return null;let i=t.indexOf(s);return{prefix:t.slice(0,i).map((o)=>o.kind==="literal"?o.value:"").join(""),quoted:s.quoted,suffix:t.slice(i+1).map((o)=>o.kind==="literal"?o.value:"").join(""),variable:s.variable}},ur=(e)=>{let t=e.parts??[];if(t.length!==1||t[0]?.type!=="DoubleQuoted")return null;let n=t[0].parts;if(n.length<1||n.length>2)return null;let[r]=n,s=r===void 0?null:kt(r);if(s===null)return null;let[,i]=n;if(i!==void 0&&i.type!=="Literal")return null;return{variable:s,suffix:i?.value??""}},wt=(e,t)=>{let n=ur(e);if(n===null||!t.temps.has(n.variable))return null;let{suffix:r,variable:s}=n;if(r!==""&&!r.startsWith("/")||r.split("/").includes("..")||r.includes("\x00"))return null;return{source:e.text,value:`/private/tmp/tripwire-provenance/${s}${r}`,kind:"trusted-temp-path",range:_e(e),quoted:!0,variable:s}},Ne=(e,t)=>{let n=vt(e);if(n===null)return null;let{prefix:r,quoted:s,suffix:i,variable:o}=n,a=t.bindings.get(o);if(a===void 0||a.kind==="dynamic"||a.kind==="trusted-temp-path"||a.kind==="background-pid"&&(r!==""||i!=="")||i.split("/").includes("..")||r.split("/").includes(".."))return null;let l=`${r}${a.value}${i}`;if(!s&&(/\s/u.test(l)||l.includes("*")||l.includes("?")||l.includes("[")))return null;return{...a,source:e.text,value:l,range:_e(e),quoted:s,variable:o}},Oe=(e,t)=>{let n=vt(e);if(!t.backgroundPidAvailable||n?.variable!=="!"||n.prefix!==""||n.suffix!=="")return null;return{source:e.text,value:"__tripwire_background_pid__",kind:"background-pid",range:_e(e),quoted:n.quoted,variable:"!"}};import{parse as je}from"unbash";var Z=(e)=>({start:e.pos,end:e.end}),U=(e)=>{let t=e.lastIndexOf("/");return t===-1?e:e.slice(t+1)},X=(e)=>{if(e.name===void 0||e.prefix.length>0||e.redirects.length>0||!L(e.name)||e.suffix.some((t)=>!L(t)))return null;return[e.name,...e.suffix].map((t)=>({source:t.text,value:t.value,kind:"literal",range:Z(t),quoted:(t.parts??[]).some((n)=>n.type==="SingleQuoted"||n.type==="DoubleQuoted"||n.type==="AnsiCQuoted"||n.type==="LocaleString")}))};var j=new Set(["sh","bash","zsh","dash","ksh","ash"]),Ct=new Set(["command","exec","env","time","nohup","setsid","nice","ionice","chronic","stdbuf","unbuffer","taskset","sudo","doas","xargs","watch"]),xt={env:new Set(["-u","--unset","-C","--chdir","-S","--split-string","--block-signal"]),time:new Set(["-f","--format","-o","--output"]),nice:new Set(["-n","--adjustment"]),ionice:new Set(["-c","--class","-n","--classdata","-p","--pid"]),stdbuf:new Set(["-i","--input","-o","--output","-e","--error"]),sudo:new Set(["-u","--user","-g","--group","-C","--close-from","-D","--chdir","-h","--host","-p","--prompt","-r","--role","-t","--type","-U","--other-user","-R","--chroot","-T","--command-timeout"]),doas:new Set(["-a","-C","-u"]),xargs:new Set(["-I","-i","-J","-n","--max-args","-P","--max-procs","-s","--max-chars","-L","--max-lines","-E","--eof","-d","--delimiter","-a","--arg-file","--replace"]),watch:new Set(["-n","--interval"])},Fe=(e,t,n)=>{let r=t;while(r<e.length){let s=e[r]?.value;if(s===void 0)break;if(n.has(s))r+=2;else if(s.includes("=")&&n.has(s.slice(0,s.indexOf("="))))r+=1;else if(s==="--")return r+1;else if(s.startsWith("-")&&s!=="-")r+=1;else break}return r},me=(e)=>{let t=Object.hasOwn(xt,e.head)?xt[e.head]??new Set:new Set,n=Fe(e.words,1,t);if(e.head==="env")while(/^[A-Za-z_][A-Za-z0-9_]*=/u.test(e.words[n]?.value??""))n+=1;return n},Y=(e,t)=>{for(let n=1;n<e.length-1;n+=1){let r=e[n]?.value;if(r!==void 0&&(r===`-${t}`||r.startsWith("-")&&!r.startsWith("--")&&r.slice(1).includes(t)))return n+1}return null},ge=(e,t,n=1)=>{for(let r=n;r<e.length;r+=1){let s=e[r]?.value;if(s!==void 0){if(t.has(s))return r+1<e.length?r+1:null;for(let i of t)if(s.startsWith(`${i}=`))return r}}return null},fr=new Set(["-e","--extension","-t","--type","-E","--exclude","-d","--max-depth","--min-depth","--exact-depth","-c","--color","--changed-within","--changed-before","-S","--size","-o","--owner","-j","--threads","-g","--glob","--format","--max-results","--ignore-file","--search-path","--base-directory","--path-separator","--and"]),pr=(e)=>e==="/"||e==="~"||e==="."||e===".."||e.startsWith("/")||e.startsWith("~/")||e.startsWith("./")||e.startsWith("../")||/^\$\{?HOME\}?(?:\/|$)/u.test(e),Et=(e)=>{if(e==="/")return 100;if(e==="~"||/^\$\{?HOME\}?(?:\/|$)/u.test(e))return 90;if(/^\/(?:etc|usr|bin|sbin|System|Library|var|boot|root|home)(?:\/|$)/u.test(e))return 80;if(e.startsWith("/Users/"))return 70;if(e.startsWith("/"))return 60;if(e.startsWith("../"))return 40;return 10},Rt=(e,t,n)=>{let r=[];for(let s=1;s<t;s+=1){let i=e[s];if(i!==void 0&&n==="fd"&&fr.has(i.value))s+=1;else if(i?.value.startsWith("-")===!0){if(n==="find")break}else if(i!==void 0&&pr(i.value))r.push(i)}return r.sort((s,i)=>Et(i.value)-Et(s.value)),r[0]??{source:".",value:".",kind:"literal",range:e[0]?.range??{start:0,end:0},quoted:!1}},He=new Set(["-x","-X","--exec","--exec-batch"]),Pt=new Set(["{}","{/}","{//}","{.}","{/.}"]),At=new Set(["-exec","-execdir","-ok","-okdir"]),It=new Set(["{}"]),_t=new Set(["run","proxy","err","test","summary"]),Ve={timeout:{valueFlags:new Set(["-s","--signal","-k","--kill-after"]),skipPositionals:1,shellFlag:!1},gtimeout:{valueFlags:new Set(["-s","--signal","-k","--kill-after"]),skipPositionals:1,shellFlag:!1},chroot:{valueFlags:new Set(["--userspec","--groups"]),skipPositionals:1,shellFlag:!1},flock:{valueFlags:new Set(["-w","--wait","--timeout","-E","--conflict-exit-code"]),skipPositionals:1,shellFlag:!0},su:{valueFlags:new Set,skipPositionals:0,shellFlag:!0}};var hr=new Set(["-B","-b","-c","-D","-E","-e","-F","-I","-i","-J","-L","-l","-m","-O","-o","-p","-Q","-R","-S","-W","-w"]),mr=(e,t)=>{let n=e[t];if(n===void 0)return{nextIndex:t,opaqueConfig:void 0,source:void 0};if(n.value==="-F"||n.value.startsWith("-F"))return{nextIndex:n.value==="-F"?t+1:t,opaqueConfig:n,source:void 0};let r=t,s;if(n.value==="-o")r+=1,s=e[r];else if(n.value.startsWith("-o"))s={...n,source:n.source.slice(2),value:n.value.slice(2)};let i=s===void 0?null:/^(?<key>[A-Za-z]+)(?:=|\s+)(?<source>[\s\S]+)$/u.exec(s.value),o=i?.groups?.key?.toLowerCase(),a=i?.groups?.source,l=s!==void 0&&a!==void 0&&a!=="none"&&o!==void 0&&["localcommand","proxycommand","remotecommand"].includes(o)?{...s,source:a,value:a}:void 0;return{nextIndex:r,opaqueConfig:void 0,source:l}},gr=(e)=>{let t=1;while(t<e.length){let r=e[t];if(r?.kind!=="literal")return null;if(r.value==="--"){t+=1;break}if(hr.has(r.value)){if(e[t+1]?.kind!=="literal")return null;t+=2}else if(r.value.startsWith("-")&&r.value!=="-")t+=1;else break}return e[t]?.kind==="literal"?t+1:null};class $e{#n;#e;constructor(e,t){this.#e=e,this.#n=[...t].toSorted((n,r)=>r.command.length-n.command.length)}isCarrierCommand(e){let t=X(e);return t!==null&&this.#i(t)!==null}inspect(e,t,n){let r=this.#i(e.words);if(r===null)return!1;this.#l(r.words,t,n);let s=gr(r.words);if(s===null){if(r.words.some((c)=>c.value==="-Q"||c.value==="-V"))return!0;return this.#e.addDiagnostic("dynamic-shell-source","Tripwire cannot identify the SSH destination and remote command safely.",e.range),!0}let i=r.words.slice(s);if(i.length===0){let c=r.words.some((h)=>h.value==="-N"||h.value==="-G"||h.value==="-Q"||h.value==="-V"||h.value==="-W"||h.value.startsWith("-W"));if(!n.inspectedPipelineInput&&!c)this.#e.addDiagnostic("dynamic-shell-source","An interactive remote shell has no command source that Tripwire can inspect.",e.range);return!0}let o=i.find((c)=>c.kind!=="literal");if(o!==void 0)return this.#e.addDiagnostic("dynamic-shell-source","The remote shell command is computed at runtime.",o.range),!0;if(n.inspectedPipelineInput&&j.has(U(i[0]?.value??""))&&Y(i,"c")===null)return this.#e.emitSynthetic(i,e,t,{...n,inspectedPipelineInput:!0}),!0;let[a]=i,l=i.at(-1);if(a!==void 0&&l!==void 0){let c=i.map((h)=>h.value).join(" ");this.#e.inspectShellSource({source:c,value:c,kind:"literal",range:{start:a.range.start,end:l.range.end},quoted:!0},t,{...n,inspectedPipelineInput:!1})}return!0}#i(e){let[t]=e;if(t!==void 0&&U(t.value)==="ssh")return{kind:"ssh",words:e};for(let n of this.#n)if(n.command.every((r,s)=>e[s]?.value===r)&&n.command.length<=e.length){let[r]=e;if(r===void 0)return null;return{kind:n.equivalentTo,words:[{...r,source:n.equivalentTo,value:n.equivalentTo},...e.slice(n.command.length)]}}return null}#l(e,t,n){for(let r=1;r<e.length;r+=1){let s=mr(e,r);if(r=s.nextIndex,s.opaqueConfig!==void 0)this.#e.addDiagnostic("dynamic-shell-source","An explicit SSH configuration file may contain executable commands that Tripwire has not inspected.",s.opaqueConfig.range);else if(s.source!==void 0)this.#e.inspectShellSource(s.source,t,{...n,inspectedPipelineInput:!1})}}}var yr=(e,t)=>{let n=X(e),r=X(t);if(n===null||r===null||U(r[0]?.value??"")!=="base64"||r.length!==2||!["-d","-D","--decode"].includes(r[1]?.value??""))return null;let s=U(n[0]?.value??""),i=n.slice(1).map((o)=>o.value);if(s==="printf"&&i.length===2){let[o,a]=i;return o==="%s"||o===String.raw`%s\n`?a??null:null}if(s==="echo"){let o=i.filter((a)=>a!=="-n");return o.length===1?o[0]??null:null}return null},br=(e)=>{if(e.length===0||e.length>1048576||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(e))return null;let t=Buffer.from(e,"base64");if(t.toString("base64")!==e||t.includes(0))return null;try{return new TextDecoder("utf-8",{fatal:!0}).decode(t)}catch{return null}};class Le{#n;#e;constructor(e,t){this.#e=e,this.#n=t}inspect(e,t,n){let r=new Set;return this.#i(e,t,n,r),this.#l(e,t,n,r),r}#i(e,t,n,r){for(let s=0;s<e.length-1;s+=1){let i=e[s],o=e[s+1];if(i?.type==="Command"&&o?.type==="Command"&&o.name!==void 0&&j.has(U(o.name.value))){for(let a of i.redirects)if((a.operator==="<<"||a.operator==="<<-")&&a.content!==void 0)this.#e.inspectShellSource({source:a.content,value:a.content,kind:"literal",range:Z(a),quoted:a.heredocQuoted===!0},t,n),r.add(s+1)}}}#l(e,t,n,r){for(let s=0;s<e.length-2;s+=1){let i=e[s],o=e[s+1],a=e[s+2];if(i?.type==="Command"&&o?.type==="Command"&&a?.type==="Command"&&this.#d(a)){let l=yr(i,o),c=l===null?null:br(l);if(c!==null)this.#e.inspectShellSource({source:c,value:c,kind:"literal",range:Z(i),quoted:!0},t,n),r.add(s+2)}}}#d(e){let t=X(e);if(t===null)return!1;if(j.has(U(t[0]?.value??"")))return Y(t,"c")===null;return this.#n.isCarrierCommand(e)}}class Be{#n;#e;#i;constructor(e,t){this.#e=e,this.#n=new $e(e,t),this.#i=new Le(e,this.#n)}inspectShellInput(e,t,n,r){if(!j.has(e.head))return!1;let s=r.inspectedPipelineInput;for(let[i,o]of t.entries())if((o.operator==="<<"||o.operator==="<<-")&&o.content!==void 0){let a={source:o.content,value:o.content,kind:"literal",range:Z(o),quoted:o.heredocQuoted===!0};this.#e.inspectShellSource(a,n,r),s=!0}else if(o.operator==="<<<")this.#e.inspectShellSource(e.redirects[i]?.target,n,r),s=!0;else if(o.operator==="<")this.#e.addDiagnostic("dynamic-shell-source","A shell wrapper reads program source from a file that Tripwire has not inspected.",Z(o));return s}inspectWrapper(e,t,n,r=!1){if(e.head==="eval"||this.#n.inspect(e,t,n)||this.#l(e,t,n,r)||this.#d(e,t,n))return;this.#u(e,t,n)}inspectPipelineInputs(e,t,n){return this.#i.inspect(e,t,n)}#l(e,t,n,r){if(!j.has(e.head))return!1;let s=Y(e.words,"c");if(s!==null)return this.#e.inspectShellSource(e.words[s],t,n),!0;if(!(r||e.tokens.includes("-n")||e.tokens.includes("--noexec")||e.tokens.includes("--help")||e.tokens.includes("--version")))this.#e.addDiagnostic("dynamic-shell-source","A shell wrapper has no inline program source that Tripwire can inspect.",e.range);return!0}#d(e,t,n){switch(e.head){case"trap":return this.#p(e,t,n),!0;case"script":{let r=ge(e.words,new Set(["-c","--command"]));if(r!==null)this.#e.inspectShellSource(e.words[r],t,n);return!0}case"watch":return this.#t(e,t,n),!0;case"xargs":return this.#m(e,t,n),!0;default:return!1}}#t(e,t,n){let r=me(e),s=e.words.slice(r);if(e.tokens.includes("-x")||e.tokens.includes("--exec")){this.#e.emitSynthetic(s,e,t,n);return}let[i]=s,o=s.at(-1),a=s.find((l)=>l.kind==="dynamic");if(a!==void 0){this.#e.addDiagnostic("dynamic-shell-source","watch receives command text that is computed at runtime.",a.range);return}if(i!==void 0&&o!==void 0){let l=s.map((c)=>c.value).join(" ");this.#e.inspectShellSource({source:l,value:l,kind:"literal",range:{start:i.range.start,end:o.range.end},quoted:!0},t,n)}}#u(e,t,n){if(Ct.has(e.head)){let r=me(e);this.#e.emitSynthetic(e.words.slice(r),e,t,n)}else if(e.head==="fd"||e.head==="fdfind")this.#h(e,He,Pt,t,n);else if(e.head==="find"||e.head==="gfind")this.#h(e,At,It,t,n);else if(e.head==="rtk")this.#r(e,t,n);else{let r=Object.hasOwn(Ve,e.head)?Ve[e.head]:void 0;if(r!==void 0)this.#f(e,r,t,n)}}#p(e,t,n){let[,r,s]=e.words,i=r?.value==="--"?s:r;if(i===void 0||i.value===""||i.value==="-"||i.value==="-p"||i.value==="-l")return;this.#e.inspectShellSource(i,t,n)}#m(e,t,n){let r=me(e),s=e.words.slice(r),i=s[0]??{kind:"literal",quoted:!1,range:e.range,source:"echo",value:"echo"},o={kind:"dynamic",quoted:!1,range:e.range,source:"<xargs-input>",value:K};this.#e.emitSynthetic([i,...s.slice(1),o],e,t,n)}#h(e,t,n,r,s){for(let i=1;i<e.words.length;i+=1){let o=e.words[i];if(o!==void 0&&t.has(o.value)){let a=[],l=Rt(e.words,i,t===He?"fd":"find");for(let c=i+1;c<e.words.length;c+=1){let h=e.words[c];if(h===void 0||h.value===";"||h.value==="+")break;a.push(n.has(h.value)?l:h)}this.#e.emitSynthetic(a,e,r,s)}}}#r(e,t,n){let r=1;while(e.words[r]?.value.startsWith("-")===!0)r+=1;let s=e.words[r]?.value;if(s===void 0)return;if(_t.has(s)){let i=ge(e.words,new Set(["-c","--command"]),r+1);if(i!==null){this.#e.inspectShellSource(e.words[i],t,n);return}this.#e.emitSynthetic(e.words.slice(r+1),e,t,n);return}this.#e.emitSynthetic(e.words.slice(r),e,t,n)}#f(e,t,n,r){if(t.shellFlag){let i=ge(e.words,new Set(["-c","--command"]));if(i!==null){this.#e.inspectShellSource(e.words[i],n,r);return}}let s=Fe(e.words,1,t.valueFlags);s+=t.skipPositionals,this.#e.emitSynthetic(e.words.slice(s),e,n,r)}}var W=(e)=>({start:e.pos,end:e.end}),Sr=(e)=>{let t=e.lastIndexOf("/");return t===-1?e:e.slice(t+1)},ye=(e)=>{let[t,...n]=e,r=new Map;if(t===void 0)return r;for(let[s,i]of t)if(n.every((o)=>o.get(s)===i))r.set(s,i);return r},Me=(e)=>{let[t,...n]=e;if(t===void 0)return De();return{aliases:ye([t.aliases,...n.map((r)=>r.aliases)]),backgroundPidAvailable:[t,...n].every((r)=>r.backgroundPidAvailable),bindings:ye([t.bindings,...n.map((r)=>r.bindings)]),functions:ye([t.functions,...n.map((r)=>r.functions)]),temps:ye([t.temps,...n.map((r)=>r.temps)])}},Ue=(e,t)=>{e.backgroundPidAvailable=t.backgroundPidAvailable,e.aliases.clear();for(let[n,r]of t.aliases)e.aliases.set(n,r);e.bindings.clear();for(let[n,r]of t.bindings)e.bindings.set(n,r);e.functions.clear();for(let[n,r]of t.functions)e.functions.set(n,r);e.temps.clear();for(let[n,r]of t.temps)e.temps.set(n,r)},be=(e,t,n)=>{for(let r of e.keys())if(!n.has(r)&&!t.has(r))e.delete(r);for(let[r,s]of t)if(!n.has(r))e.set(r,s)},kr=(e,t,n)=>{be(e.aliases,t.aliases,new Set),be(e.functions,t.functions,new Set),be(e.bindings,t.bindings,n),be(e.temps,t.temps,n),e.backgroundPidAvailable=t.backgroundPidAvailable},Dt=(e,t)=>{if(t.temps.delete(e.name),t.bindings.delete(e.name),e.trustedTemp)t.temps.set(e.name,{variable:e.name});else if(e.binding!==void 0)t.bindings.set(e.name,e.binding)},vr=(e,t,n)=>{if(e.append===!0||e.index!==void 0||e.array!==void 0||e.value===void 0)return!1;if(t?.kind==="literal")return L(e.value)||We(e.value,n)!==null||Ne(e.value,n)!==null;return t?.kind==="background-pid"&&Oe(e.value,n)!==null};class Tt{#n;#e=[];#i=[];#l=[];#d=new Set;#t=[];#u=new WeakSet;#p=new Set;#m=new Set;#h=[];#r;#f;#g=1;#k=1;constructor(e,t){this.#n=e,this.#f=t,this.#r=new Be({addDiagnostic:(n,r,s)=>{this.#o(n,r,s)},emitSynthetic:(n,r,s,i)=>{this.#R(n,r,s,i)},inspectShellSource:(n,r,s)=>{this.#H(n,r,s)}},t.executionCarrierAliases??[])}analyze(e){let t=De(),n=(r,s)=>{t.bindings.set(r,{source:s,value:s,kind:"literal",range:{start:0,end:0},quoted:!0,variable:r})};if(this.#f.cwd!==void 0)n("PWD",this.#f.cwd);for(let[r,s]of(this.#f.positionalArguments??[]).entries())n((r+1).toString(),s);return this.#y(e,t,{inspectedPipelineInput:!1,source:this.#n,pipeline:void 0}),{source:this.#n,invocations:this.#e,redirects:this.#i,diagnostics:this.#l,environmentAssignments:[...this.#d],hasBypass:this.#$()}}#o(e,t,n){this.#l.push({kind:e,message:t,range:n})}#y(e,t,n){if(this.#u.has(e))return;this.#u.add(e);let r=e.source??n.source;for(let s of e.errors??[])this.#o("parse-error",s.message,{start:s.pos,end:s.pos});this.#v(e.commands,t,{...n,source:r})}#v(e,t,n){for(let r of e)this.#x(r,t,n)}#x(e,t,n){if(this.#w(e.redirects,t,n),this.#a(e.command,t,n),e.background===!0)t.backgroundPidAvailable=!0}#a(e,t,n){if(this.#P(e,t,n)||this.#A(e,t,n)||this.#I(e,t,n))return;this.#o("parse-error",`Unknown Bash AST node: ${e.type}`,W(e))}#P(e,t,n){if(e.type==="Statement")return this.#x(e,t,n),!0;if(e.type==="Command")return this.#_(e,t,n),!0;if(e.type==="CompoundList")return this.#v(e.commands,t,n),!0;if(e.type==="Pipeline"){let r=this.#k;this.#k+=1;let s=this.#r.inspectPipelineInputs(e.commands,t,n);for(let[i,o]of e.commands.entries())this.#a(o,v(t),{...n,inspectedPipelineInput:s.has(i),pipeline:{id:r,index:i}});return!0}if(e.type==="AndOr"){let r=v(t);for(let s of e.commands)this.#a(s,r,n);return Ue(t,Me([t,r])),!0}return!1}#A(e,t,n){if(e.type==="If"){let r=v(t);this.#a(e.clause,r,n);let s=v(r);this.#a(e.then,s,n);let i=v(r);if(e.else!==void 0)this.#a(e.else,i,n);return Ue(t,Me([s,i])),!0}if(e.type==="For"||e.type==="Select"){let r=this.#s(e.name,t,n),s=e.wordlist.map((i)=>this.#s(i,t,n));if(r.kind==="literal"&&/^[A-Za-z_][A-Za-z0-9_]*$/u.test(r.value)&&s.length>0&&s.length<=32&&s.every((i)=>i.kind==="literal"))for(let i of s){let o=v(t);o.temps.delete(r.value),o.bindings.set(r.value,i),this.#a(e.body,o,n)}else{let i=v(t);i.temps.delete(r.value),i.bindings.delete(r.value),this.#a(e.body,i,n)}return!0}if(e.type==="ArithmeticFor")return this.#c(e.initialize,t,n),this.#c(e.test,t,n),this.#c(e.update,t,n),this.#a(e.body,v(t),n),!0;if(e.type==="While"){let r=v(t);return this.#a(e.clause,r,n),this.#a(e.body,r,n),!0}if(e.type==="Function"){let r=this.#s(e.name,t,n);if(r.kind!=="literal"||!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(r.value))this.#o("function","The function name is not static.",W(e));else t.functions.set(r.value,e);return!0}return!1}#I(e,t,n){if(e.type==="Subshell")return this.#a(e.body,v(t),n),!0;if(e.type==="BraceGroup")return this.#a(e.body,t,n),!0;if(e.type==="Case"){this.#s(e.word,t,n);let r=[v(t)];for(let s of e.items){for(let o of s.pattern)this.#s(o,t,n);let i=v(t);this.#a(s.body,i,n),r.push(i)}return Ue(t,Me(r)),!0}if(e.type==="Coproc"){if(e.name!==void 0)this.#s(e.name,t,n);return this.#w(e.redirects,t,n),this.#a(e.body,v(t),n),!0}if(e.type==="TestCommand")return this.#S(e.expression,t,n),!0;if(e.type==="ArithmeticCommand")return this.#c(e.expression,t,n),!0;return!1}#_(e,t,n){let r=e.prefix.flatMap((c)=>{let h=this.#D(c,t,n);return h===null?[]:[h]});if(e.name===void 0)for(let c of r)Dt(c,t);let s=this.#w(e.redirects,t,n);if(e.name===void 0)return;let i=this.#s(e.name,t,n),o=e.suffix.flatMap((c)=>this.#E(c,t,n));if(i.kind==="literal"&&t.functions.has(i.value)){this.#W(i.value,o,r,t,n);return}if(i.kind==="literal"&&t.aliases.has(i.value)){this.#N(i.value,o,t,n,W(e));return}let a=this.#C([i,...o],s,W(e),n,!1);if(a===null)return;if(a.head==="alias"){this.#O(a,t);return}if(a.head==="unalias"){this.#F(a,t);return}let l=this.#r.inspectShellInput(a,e.redirects,t,n);this.#r.inspectWrapper(a,t,n,l),this.#V(a,t)}#D(e,t,n){for(let o of e.indexParts??[])this.#b(o,t,n);for(let o of e.array??[])this.#s(o,t,n);let r=e.value===void 0?void 0:this.#s(e.value,t,n);if(e.name===void 0)return null;this.#d.add(e.name);let s=e.append!==!0&&e.index===void 0&&e.array===void 0&&e.value!==void 0&&St(e.value,t);return{binding:vr(e,r,t)?r:void 0,name:e.name,trustedTemp:s}}#E(e,t,n){let r=this.#s(e,t,n);if(r.kind!=="literal"||r.quoted||!/[*?[]/u.test(r.value))return[r];try{let s=[...new Bun.Glob(r.value).scanSync({onlyFiles:!1,dot:!0})];if(s.length===0)return[r];let i=[];for(let o of s)i.push({source:r.source,value:o,kind:r.kind,range:r.range,quoted:r.quoted});return i}catch{return[r]}}#s(e,t,n){for(let a of e.parts??[])this.#b(a,t,n);let r=wt(e,t);if(r!==null)return r;let s=Oe(e,t);if(s!==null)return s;let i=Ne(e,t);if(i!==null)return i;if(L(e))return{source:e.text,value:e.value,kind:"literal",range:W(e),quoted:(e.parts??[]).some((a)=>Te(a))};let o=We(e,t);if(o!==null)return{source:e.text,value:o,kind:"literal",range:W(e),quoted:!0};return{source:e.text,value:e.value,kind:"dynamic",range:W(e),quoted:(e.parts??[]).some((a)=>Te(a))}}#b(e,t,n){switch(e.type){case"Literal":case"SingleQuoted":case"AnsiCQuoted":case"SimpleExpansion":return;case"DoubleQuoted":case"LocaleString":{for(let r of e.parts)this.#b(r,t,n);return}case"ParameterExpansion":{this.#T(e,t,n);return}case"CommandExpansion":case"ProcessSubstitution":{if(e.script!==void 0)this.#y(e.script,v(t),n);return}case"ArithmeticExpansion":{this.#c(e.expression,t,n);return}case"ExtendedGlob":case"BraceExpansion":{for(let r of e.parts??[])this.#b(r,t,n);break}default:break}}#T(e,t,n){for(let r of e.indexParts??[])this.#b(r,t,n);if(e.operand!==void 0)this.#s(e.operand,t,n);if(e.slice!==void 0){if(this.#s(e.slice.offset,t,n),e.slice.length!==void 0)this.#s(e.slice.length,t,n)}if(e.replace!==void 0)this.#s(e.replace.pattern,t,n),this.#s(e.replace.replacement,t,n)}#c(e,t,n){if(e===void 0)return;switch(e.type){case"ArithmeticBinary":{this.#c(e.left,t,n),this.#c(e.right,t,n);return}case"ArithmeticUnary":{this.#c(e.operand,t,n);return}case"ArithmeticTernary":{this.#c(e.test,t,n),this.#c(e.consequent,t,n),this.#c(e.alternate,t,n);return}case"ArithmeticGroup":{this.#c(e.expression,t,n);return}case"ArithmeticWord":{for(let r of e.parts??[])this.#b(r,t,n);return}case"ArithmeticCommandExpansion":{if(e.script!==void 0)this.#y(e.script,v(t),n);break}default:break}}#S(e,t,n){switch(e.type){case"TestUnary":{this.#s(e.operand,t,n);return}case"TestBinary":{this.#s(e.left,t,n),this.#s(e.right,t,n);return}case"TestLogical":{this.#S(e.left,t,n),this.#S(e.right,t,n);return}case"TestNot":{this.#S(e.operand,t,n);return}case"TestGroup":{this.#S(e.expression,t,n);break}default:break}}#w(e,t,n){let r=[];for(let s of e){if(s.body!==void 0)this.#t.push(W(s.body)),this.#s(s.body,t,n);else if(s.content!==void 0){let l=n.source.indexOf(s.content,s.end);if(l!==-1)this.#t.push({start:l,end:l+s.content.length})}let i=s.target===void 0?{source:"",value:K,kind:"dynamic",range:W(s),quoted:!1}:this.#s(s.target,t,n),o;if(s.operator==="<<-")o="<<";else if(s.operator===">|")o=">";else o=s.operator;let a={op:o,target:i,range:W(s),...s.content!==void 0&&{heredoc:{content:s.content,quoted:s.heredocQuoted===!0}}};r.push(a),this.#i.push(a)}return r}#C(e,t,n,r,s){let[i]=e;if(i===void 0)return null;if(i.kind!=="literal")return this.#o("dynamic-executable","The executable name is computed at runtime.",i.range),null;let o=e.map((h)=>h.value),a=o.slice(1).filter((h)=>h.startsWith("-")&&h!=="-"),l=o.slice(1).filter((h)=>!h.startsWith("-")||h==="-"),c={id:this.#g,head:Sr(i.value),words:e,tokens:o,args:l,flags:a,redirects:t,raw:s?e.map((h)=>h.source).join(" "):r.source.slice(n.start,n.end),range:n,pipeline:r.pipeline,synthetic:s};if(this.#g+=1,this.#e.push(c),c.head==="eval")this.#o("eval","`eval` can turn computed data into shell syntax.",n);return c}#W(e,t,n,r,s){let i=r.functions.get(e);if(i===void 0)return;if(this.#m.has(e)){this.#o("function",`Recursive function call: ${e}`,W(i));return}this.#m.add(e);let o=v(r),a=new Set(n.map((c)=>c.name));for(let c of n)Dt(c,o);for(let c of o.bindings.keys())if(/^[0-9]+$/u.test(c))o.bindings.delete(c);for(let[c,h]of t.entries())o.bindings.set(String(c+1),h);let l={localNames:new Set};this.#h.push(l);try{this.#w(i.redirects,o,s),this.#a(i.body,o,s);let c=new Set([...a,...l.localNames]);for(let h of o.bindings.keys())if(/^[0-9]+$/u.test(h))c.add(h);kr(r,o,c)}finally{this.#h.pop(),this.#m.delete(e)}}#N(e,t,n,r,s){let i=n.aliases.get(e);if(i===void 0)return;if(this.#p.has(e)){this.#o("alias",`Recursive alias expansion: ${e}`,s);return}this.#p.add(e);try{let o=je(i),[a]=o.commands,l=a?.command;if((o.errors?.length??0)>0||o.commands.length!==1||a?.background===!0||a?.redirects.length!==0||l?.type!=="Command"||l.name===void 0||l.prefix.length>0||l.redirects.length>0){this.#y(o,v(n),{...r,source:i,pipeline:void 0}),this.#o("alias",`Alias ${e} has compound expansion whose call arguments cannot be bound safely.`,s);return}let c={...r,source:i},h=[this.#s(l.name,n,c),...l.suffix.flatMap((A)=>this.#E(A,n,c)),...t],x={id:0,head:e,words:[],tokens:[],args:[],flags:[],redirects:[],raw:e,range:s,pipeline:r.pipeline,synthetic:!0};this.#R(h,x,n,r)}finally{this.#p.delete(e)}}#O(e,t){for(let n of e.words.slice(1))if(n.kind==="literal"){let r=n.value.indexOf("=");if(r!==-1){let s=n.value.slice(0,r);if(/^[A-Za-z_][A-Za-z0-9_]*$/u.test(s))t.aliases.set(s,n.value.slice(r+1));else this.#o("alias",`Invalid alias name: ${s}`,n.range)}}else this.#o("alias","An alias definition is computed at runtime.",n.range)}#F(e,t){if(e.tokens.includes("-a")){t.aliases.clear();return}for(let n of e.words.slice(1))if(n.kind==="literal")t.aliases.delete(n.value),this.#p.delete(n.value)}#R(e,t,n,r){let s=this.#C(e,[],t.range,{...r,pipeline:t.pipeline},!0);if(s!==null)this.#r.inspectWrapper(s,n,r,r.inspectedPipelineInput)}#H(e,t,n){if(e?.kind!=="literal"){this.#o("dynamic-shell-source","A shell wrapper receives source that is computed at runtime.",e?.range??{start:0,end:0});return}try{this.#y(je(e.value),v(t),{...n,inspectedPipelineInput:!1,source:e.value,pipeline:void 0})}catch(r){this.#o("parse-error",r instanceof Error?r.message:"The nested shell source could not be parsed.",e.range)}}#V(e,t){if(e.head==="export"&&e.flags.some((r)=>r==="-f"||/^-[^-]*f/u.test(r)))this.#o("function","Exported functions can execute in a nested shell outside the local call binding.",e.range);if(e.head==="unset"){for(let r of e.words.slice(1))if(/^[A-Za-z_][A-Za-z0-9_]*$/u.test(r.value))t.temps.delete(r.value),t.bindings.delete(r.value);return}if(!["export","readonly","declare","typeset","local"].includes(e.head))return;let n=this.#h.at(-1);for(let r of e.words.slice(1)){let s=/^(?<name>[A-Za-z_][A-Za-z0-9_]*)/u.exec(r.source)?.groups?.name;if(s!==void 0){if(this.#d.add(s),e.head==="local")n?.localNames.add(s);t.temps.delete(s),t.bindings.delete(s)}}}#$(){let e=this.#n.split("");for(let t of this.#t)for(let n=t.start;n<t.end&&n<e.length;n+=1)if(e[n]!==`
3
+ `&&e[n]!=="\r")e[n]=" ";return/(?<prefix>^|\s)#\s*tripwire-allow:[ \t]*\S[^\r\n]*/u.test(e.join(""))}}var ae=(e,t={})=>{try{return new Tt(e,t).analyze(je(e))}catch(n){return{source:e,invocations:[],redirects:[],diagnostics:[{kind:"parse-error",message:n instanceof Error?n.message:"The Bash parser failed.",range:{start:0,end:e.length}}],hasBypass:!1}}};var wr=["dist","build","_build","out","target",".next",".nuxt",".svelte-kit",".output",".astro",".angular",".vite",".parcel-cache",".turbo",".vercel",".netlify",".fly",".wrangler",".serverless","coverage",".nyc_output",".cache",".ruff_cache",".mypy_cache",".pytest_cache",".ty_cache",".tox","__pycache__",".venv","venv","node_modules",".gradle","DerivedData",".bundle",".cargo-target","tmp",".tmp",".state",".terraform",".yarn/cache",".yarn/install-state.gz",".pnpm-store",".bun"],Wt=["/tmp","/var/tmp","/var/folders","/private/tmp","/private/var/tmp","/private/var/folders"],xr=(e)=>e.startsWith("./")?e.slice(2):e,ee=(e,t=[],n=[])=>{if(e===""||e===K)return!1;let r=xr(e);if(r===".."||r.startsWith("../")||r.includes("/../"))return!1;for(let s of[...Wt,...n])if(r===s||r.startsWith(`${s}/`))return!0;for(let s of[...wr,...t])if(r===s||r.startsWith(`${s}/`))return!0;return!1},Nt=(e=[],t=[])=>{let n={"build outputs":["dist","build","_build","out","target"],"js framework outputs":[".next",".nuxt",".svelte-kit",".output",".astro",".angular",".vite",".parcel-cache",".turbo",".vercel",".netlify",".fly",".wrangler",".serverless"],"tests / coverage":["coverage",".nyc_output"],caches:[".cache",".ruff_cache",".mypy_cache",".pytest_cache",".ty_cache",".tox"],"language / package":["__pycache__",".venv","venv","node_modules",".gradle","DerivedData",".bundle",".cargo-target"],"tmp / state":["tmp",".tmp",".state",...Wt],iac:[".terraform"],"bundler dev":[".yarn/cache",".yarn/install-state.gz",".pnpm-store",".bun"]};if(e.length>0)n["custom relative"]=e;if(t.length>0)n["custom absolute"]=t;return Object.entries(n).map(([r,s])=>` ${r}: ${s.join(", ")}`).join(`
4
+ `)};import{readFileSync as Pr}from"fs";import{homedir as Ar}from"os";import{Cause as Ir,Data as Vt,Effect as I,Schema as p}from"effect";import{Schema as N}from"effect";var Er=N.Literal("ssh"),Cr=N.Struct({command:N.Array(N.NonEmptyString).check(N.isMinLength(1),N.isMaxLength(16)),equivalentTo:Er}),Ot=N.Struct({executionCarrierAliases:N.optional(N.Array(Cr).check(N.isMaxLength(64)))});import{Schema as w}from"effect";var E=w.Literals(["allow","warn","ask","deny"]),ze=w.Struct({"brew-mutation":w.optional(E),"chmod-777-recursive":w.optional(E),"defaults-write":w.optional(E),"dscl-mutate":w.optional(E),"launchctl-mutation":w.optional(E),"mas-mutation":w.optional(E),"no-gpg-sign":w.optional(E),"no-verify":w.optional(E),osascript:w.optional(E),"pmset-write":w.optional(E),"rsync-delete":w.optional(E),"scutil-set":w.optional(E),"security-keychain-add-write":w.optional(E),"softwareupdate-install":w.optional(E),sudo:w.optional(E),systemsetup:w.optional(E),topgrade:w.optional(E)}),Rr=(e)=>Object.hasOwn(ze.fields,e),Ft=(e,t)=>{let n=Rr(e.rule)?t[e.rule]:void 0;if(n===void 0||n===e.kind)return e;if(n==="allow")return u(e.rule);if(n==="warn")return T(e.rule,e.message);if(n==="ask")return b(e.rule,e.message);return f(e.rule,e.message)};var Ht=p.Struct({pattern:p.String,message:p.String,action:p.optional(p.Union([p.Literal("deny"),p.Literal("ask")])),requiresFlags:p.optional(p.Array(p.String)),forbidsFlagValues:p.optional(p.Array(p.Struct({flag:p.String,values:p.Array(p.String)})))}),_r=p.Struct({protectedBranches:p.optional(p.Array(p.String)),enforceConventionalCommits:p.optional(p.Boolean)}),Dr=p.Struct({relative:p.optional(p.Array(p.String)),absolute:p.optional(p.Array(p.String))}),Tr=p.Struct({argumentsIncludeAll:p.optional(p.Array(p.String)),argumentsStartWith:p.optional(p.Array(p.String)),shortFlagsIncludeAll:p.optional(p.Array(p.String))}),Wr=p.Struct({rule:p.String,executables:p.Array(p.String),action:p.Union([p.Literal("deny"),p.Literal("warn")]),message:p.String,match:p.optional(Tr)}),Nr=p.Struct({executable:p.String,timeoutMs:p.Finite.check(p.isGreaterThan(0))}),Or=p.Struct({git:p.optional(_r),safePaths:p.optional(Dr),toolPolicies:p.optional(p.Array(Wr)),blockedCommands:p.optional(p.Array(Ht)),allowedCommands:p.optional(p.Array(Ht)),ruleActions:p.optional(ze),secretScanner:p.optional(Nr),shell:p.optional(Ot)}),Se=`${Ar()}/.config/tripwire/config.json`;class $t extends Vt.TaggedError("ConfigReadError"){}class Lt extends Vt.TaggedError("ConfigParseError"){}var Fr=(e)=>e instanceof Error&&("code"in e)&&e.code==="ENOENT",Hr=(e)=>I.try({try:()=>Pr(e,"utf8"),catch:(t)=>new $t({cause:t})}).pipe(I.catchTag("ConfigReadError",(t)=>Fr(t.cause)?I.succeed(null):I.fail(t))),Vr=(e)=>I.try({try:()=>JSON.parse(e),catch:(t)=>new Lt({cause:t})}),$r=(e)=>p.decodeUnknownEffect(Or)(e,{onExcessProperty:"error"}),le=()=>({git:{protectedBranches:[],enforceConventionalCommits:!1},safePaths:{},toolPolicies:[],blockedCommands:[],allowedCommands:[],ruleActions:{},secretScanner:{executable:"betterleaks",timeoutMs:5000},shell:{executionCarrierAliases:[]}}),ce=(e)=>{let t=le();return{git:{protectedBranches:e.git?.protectedBranches??t.git.protectedBranches,enforceConventionalCommits:e.git?.enforceConventionalCommits??t.git.enforceConventionalCommits},safePaths:{...t.safePaths,...e.safePaths},toolPolicies:e.toolPolicies??t.toolPolicies,blockedCommands:e.blockedCommands??t.blockedCommands,allowedCommands:e.allowedCommands??t.allowedCommands,ruleActions:{...t.ruleActions,...e.ruleActions},secretScanner:e.secretScanner??t.secretScanner,shell:{executionCarrierAliases:e.shell?.executionCarrierAliases??t.shell.executionCarrierAliases}}},ke=(e=Se)=>I.gen(function*(){let n=yield*Hr(e);if(n===null)return{ok:!0,config:le()};let r=yield*Vr(n),s=yield*$r(r);return{ok:!0,config:ce(s)}}).pipe(I.timeout(1000),I.catchCause((t)=>{let n={ok:!1,error:Ir.pretty(t)};return I.succeed(n)})),Lr=(e=Se)=>ke(e).pipe(I.flatMap((t)=>t.ok?I.succeed(t.config):I.die(Error(`[tripwire] config load failed (${e}): ${t.error}`))));import{Result as Br,Schema as ve}from"effect";var Mr=ve.Record(ve.String,ve.Unknown),jt=new Set(["preToolUse","beforeShellExecution","beforeMCPExecution","beforeReadFile","beforeTabFileRead"]),Je=new Set(["postToolUse","postToolUseFailure","afterShellExecution","afterMCPExecution","afterFileEdit","afterTabFileEdit"]),Ke=(e)=>({kind:"cursor",eventName:e,post:Je.has(e)}),Ur=(e)=>jt.has(e)||Je.has(e),Q=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),S=(e,...t)=>{for(let n of t){let r=e[n];if(typeof r==="string")return r}return},Ze=(e,...t)=>{for(let n of t)if(n in e)return e[n];return},Ge=(e,t,...n)=>{let r=S(e,...n);if(r!==void 0)return r;if(typeof t==="string"&&n.includes("command"))return t;return Q(t)?S(t,...n):void 0},Bt=(e,t)=>{if(e===void 0)throw Error(`Cursor hook payload missing ${t}`);return e},jr=(e)=>{let t=e.toLowerCase();if(t==="bash"||t==="exec"||t==="shell"||t==="run_command")return"Bash";if(t==="read"||t==="read_file")return"Read";if(t==="write"||t==="write_file")return"Write";if(t==="edit"||t==="edit_file"||t==="multiedit"||t==="apply_patch")return"Edit";return e},zr=(e)=>e==="Read"||e==="Write"||e==="Edit",qr=(e,t)=>{let n;if(typeof e==="string")n=e;else if(Q(e))n=S(e,"command","cmd");if(t&&n===void 0)throw Error("Cursor hook payload missing command");if(typeof e==="string")return{command:e};if(!Q(e)||n===void 0)return e;return{...e,command:n}},Gr=(e,t,n)=>{let r=S(t,"file_path","filePath","path");if(n&&r===void 0)throw Error(`Cursor ${e} hook payload missing file path`);if(e==="Read")return{...t,file_path:r??""};if(e==="Write"){let s=S(t,"content","fileText","text");if(n&&s===void 0)throw Error("Cursor Write hook payload missing content");return{...t,file_path:r??"",content:s??""}}if(e==="Edit"){let s=S(t,"old_string","oldString"),i=S(t,"new_string","newString");if(n&&(s===void 0||i===void 0))throw Error("Cursor Edit hook payload missing oldString or newString");return{...t,file_path:r??"",old_string:s??"",new_string:i??""}}return t},qe=(e,t,n=!0)=>{if(e==="Bash")return qr(t,n);if(!zr(e))return t;if(!Q(t)){if(n)throw Error(`Cursor ${e} hook payload is missing tool input`);return t}return Gr(e,t,n)},Mt=(e,t)=>{if(typeof t!=="string")return t;if(e==="Bash")return{stdout:t,stderr:""};if(e==="Read")return{content:t};return{content:t}},Jr=(e,t)=>{if(e==="beforeShellExecution"||e==="afterShellExecution")return"Bash";if(e==="beforeReadFile"||e==="beforeTabFileRead"||e==="afterFileEdit"||e==="afterTabFileEdit")return e.startsWith("before")?"Read":"Edit";let n=S(t,"tool_name","toolName");if(n!==void 0)return jr(n);let r=Ze(t,"tool_input","toolInput");if(S(t,"command","cmd")!==void 0||typeof r==="string"||Q(r)&&S(r,"command","cmd")!==void 0)return"Bash";if(S(t,"file_path","filePath","path")!==void 0||Q(r)&&S(r,"file_path","filePath","path")!==void 0)return"Read";return""},Kr=(e,t)=>{if(e==="Bash")return{command:Ge(t,void 0,"command","cmd")};let n={file_path:S(t,"file_path","filePath","path")};if(e==="Write")return{...n,content:S(t,"content","fileText","text")};if(e==="Edit")return{...n,old_string:S(t,"old_string","oldString"),new_string:S(t,"new_string","newString")};return n},Zr=(e,t,n,r)=>{let s=ve.decodeResult(Mr)(n);if(Br.isFailure(s))throw Error("Cursor sent an invalid tool event");let i=s.success,o=Ze(i,"tool_input","toolInput"),a=!r;if(e==="beforeShellExecution"||e==="afterShellExecution"){let l=Ge(i,o,"command","cmd");return{command:a?Bt(l,"command"):l??""}}if(e==="beforeReadFile"||e==="beforeTabFileRead"||e==="afterFileEdit"||e==="afterTabFileEdit"){let l=Ge(i,o,"file_path","filePath","path");return qe(t,{file_path:a?Bt(l,"file path"):l??"",...Array.isArray(i.edits)&&{edits:i.edits}},a)}if(o===void 0&&["Bash","Read","Write","Edit"].includes(t))return qe(t,Kr(t,i),a);return qe(t,o,a)},Qr=(e,t,n)=>{if(e==="afterShellExecution")return Mt(t,S(n,"output")??"");let r=Ze(n,"tool_response","toolResponse","tool_output","toolOutput","result","result_json");return Mt(t,r)},Ut=(e,t,n)=>{let r=Jr(t,e),s=Zr(t,r,e,n),i=n?Qr(t,r,e):void 0,o=S(e,"cwd"),a=S(e,"conversation_id","session_id"),l=S(e,"tool_use_id");return{event:{hook_event_name:n?"PostToolUse":"PreToolUse",...r.length>0&&{tool_name:r},...s!==void 0&&{tool_input:s},...i!==void 0&&{tool_response:i},...o!==void 0&&{cwd:o},...a!==void 0&&{session_id:a},...l!==void 0&&{tool_use_id:l}},host:Ke(t)}},zt=(e,t)=>{if(t!==void 0&&!Ur(t))throw Error(`Unknown Cursor hook event "${t}"`);if(!Q(e)){if(t!==void 0)throw Error(`Cursor ${t} hook payload must be a JSON object`);return{event:e,host:{kind:"native"}}}let n=t??S(e,"hook_event_name");if(n!==void 0&&jt.has(n))return Ut(e,n,!1);if(n!==void 0&&Je.has(n))return Ut(e,n,!0);return{event:e,host:{kind:"native"}}};import{Schema as g}from"effect";var Qe=g.Struct({hook_event_name:g.String,tool_name:g.optional(g.String),tool_input:g.optional(g.Unknown),tool_response:g.optional(g.Unknown),cwd:g.optional(g.String),session_id:g.optional(g.String),turn_id:g.optional(g.String),tool_use_id:g.optional(g.String)}),te=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),Xe=(e)=>te(e)&&typeof e.command==="string",Xr=g.Union([g.String,g.Struct({code:g.String})]),Yr=g.Struct({cmd:g.String}),qt=g.decodeUnknownOption(Xr),Gt=g.decodeUnknownOption(Yr),Jt=(e)=>te(e)&&typeof e.file_path==="string"&&typeof e.old_string==="string"&&typeof e.new_string==="string",Kt=(e)=>te(e)&&typeof e.file_path==="string"&&typeof e.content==="string",Zt=(e)=>te(e)&&typeof e.file_path==="string",Qt=(e,t)=>{if(!te(t))return"";if(e==="Bash"){let n=typeof t.stdout==="string"?t.stdout:"",r=typeof t.stderr==="string"?t.stderr:"";return[n,r].filter((s)=>s.length>0).join(`
5
+ `)}if(e==="Read"){if(typeof t.content==="string")return t.content;let{file:n}=t;return te(n)&&typeof n.content==="string"?n.content:""}return typeof t.content==="string"?t.content:""};import{appendFileSync as es,mkdirSync as ts}from"fs";import{homedir as ns}from"os";import rs from"path";var Xt=`${ns()}/.claude/tripwire.log`;try{ts(rs.dirname(Xt),{recursive:!0})}catch{}var O=(e,t)=>{let n=new Date().toISOString(),r=t instanceof Error?t.stack??t.message:String(t);try{es(Xt,`[${n}] [${e}] ${r}
6
+ `)}catch{}};var ss=new Set(["unsupported-shell-structure","source-script","rm-rf-root","rm-rf-home","fork-bomb","dd-raw-device","mkfs","kill-all","diskutil-destructive","tmutil-destructive","shutdown","csrutil","nvram","kextload","spctl-disable","xattr-quarantine-bypass","topgrade","softwareupdate-install","systemsetup","scutil-set","security-keychain-destructive","no-verify","no-gpg-sign","curl-pipe-shell","gh-api-mutation","gh-destructive","flyctl-destroy","gcloud-delete","git-dynamic-subcommand","git-dynamic-mutation","git-restore-discard","git-checkout-force","git-checkout-discard","git-checkout-discard-all","git-switch-force","git-reset-hard","git-force-push","git-filter","git-update-ref","git-reflog-expire","git-symbolic-ref-write"]),ne=(e,t)=>{if(!e.hasBypass||t.kind==="allow"||ss.has(t.rule))return t;return u(t.rule)};var is=(e)=>e.tokens.slice(1).join(" "),Yt=(e,...t)=>{let n=new Set(e.flags);return t.some((r)=>n.has(r))},os=new Map([["launchctl",1],["defaults",1],["diskutil",1],["tmutil",1],["security",1],["brew",1],["mas",1],["kmutil",1],["gh",2]]),as=new Set(["dd","kill","chmod","softwareupdate","pmset","dscl","xattr","spctl","systemsetup","scutil","flyctl","gcloud"]),ls=(e)=>{if(e.head==="dd")return e.words.slice(1).some((n)=>n.kind==="dynamic"&&!n.source.startsWith("if="));if(e.head==="rsync"){let n=e.words.findIndex((s)=>s.value==="--"),r=n===-1?e.words.length:n;return e.words.slice(1,r).some((s)=>s.kind==="dynamic")}if(e.head==="gh"&&e.tokens[1]==="api"){for(let n=2;n<e.words.length;n+=1){let r=e.words[n-1]?.value,s=e.words[n];if(s?.kind==="dynamic"&&(r==="--method"||r==="-X"||s.source.startsWith("-X")||s.source.startsWith("--method=")))return!0}return!1}let t=os.get(e.head);if(t!==void 0)return e.words.slice(1,t+1).some((n)=>n.kind==="dynamic");return as.has(e.head)&&e.words.slice(1).some((n)=>n.kind==="dynamic")},cs=(e)=>{if(e.head!=="gh"||e.tokens[1]!=="api")return null;for(let t=2;t<e.tokens.length;t+=1){let n=e.tokens[t];if(n==="--method"||n==="-X")return e.tokens[t+1]?.toUpperCase()??"";if(n?.startsWith("--method=")===!0)return n.slice(9).toUpperCase();if(n?.startsWith("-X")===!0&&n.length>2)return n.slice(2).toUpperCase()}return null},ds=(e)=>{if(e.head!=="gh"||e.tokens[1]!=="api")return!1;let t=cs(e);if(t!==null&&["POST","PUT","PATCH","DELETE"].includes(t))return!0;return e.flags.some((n)=>n==="--input"||n.startsWith("--input=")||n==="-f"||n==="-F"||n==="--field"||n.startsWith("--field=")||n==="--raw-field"||n.startsWith("--raw-field="))},us=[{rule:"rm-rf-root",action:"deny",message:"rm -rf / is catastrophic. If the goal is cleaning a subdirectory, scope the path inside the project (e.g. ./dist).",match:(e)=>e.head==="rm"&&Yt(e,"-rf","-fr","-Rf","-fR")&&e.tokens.includes("/")},{rule:"rm-rf-home",action:"deny",message:"rm -rf on $HOME / ~ would erase the home directory. Refuse.",match:(e)=>e.head==="rm"&&Yt(e,"-rf","-fr","-Rf","-fR")&&e.tokens.some((t)=>/^(?<home>~|\$HOME|\$\{HOME\})$/u.test(t))},{rule:"fork-bomb",action:"deny",message:"Fork bomb pattern detected. Refuse.",match:(e,t)=>/:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/u.test(t)},{rule:"source-script",action:"deny",message:"`source` / `.` executes a file in the current shell. Refuse arbitrary sourced scripts.",match:(e)=>(e.head==="source"||e.head===".")&&e.tokens.length>1},{rule:"dd-raw-device",action:"deny",message:"dd writing to a raw block device wipes the disk. Refuse.",match:(e)=>e.head==="dd"&&/\bof=\/dev\/(?<type>disk|sd|nvme|rdisk)/iu.test(is(e))},{rule:"mkfs",action:"deny",message:"mkfs formats a filesystem. Refuse.",match:(e)=>/^mkfs(?<ext>\.[a-z0-9]+)?$/iu.test(e.head)},{rule:"kill-all",action:"deny",message:"kill -9 -1 kills every process you own. Refuse.",match:(e)=>e.head==="kill"&&e.tokens.includes("-9")&&e.tokens.includes("-1")},{rule:"chmod-777-recursive",action:"deny",message:"Recursive chmod 777 makes everything world-writable. Use 755 for directories, 644 for files, scoped narrowly.",match:(e)=>e.head==="chmod"&&e.flags.some((t)=>t.includes("R"))&&e.tokens.includes("777")},{rule:"no-verify",action:"deny",message:"--no-verify skips git hooks. Per policy: never skip hooks. Fix the underlying issue.",match:(e)=>e.tokens.includes("--no-verify")},{rule:"no-gpg-sign",action:"deny",message:"Bypassing GPG signing is off-limits unless explicitly requested.",match:(e,t)=>/--no-gpg-sign\b|-c\s+commit\.gpgsign=false/u.test(t)},{rule:"sudo",action:"ask",message:"sudo escalates privileges and is almost never needed in a coding session. If genuinely required, explain why; otherwise find a non-sudo path.",match:(e)=>e.head==="sudo"},{rule:"shutdown",action:"deny",message:"shutdown / reboot / halt control the machine. Refuse \u2014 system control should be done manually.",match:(e)=>["shutdown","reboot","halt","poweroff"].includes(e.head)},{rule:"launchctl-mutation",action:"deny",message:"launchctl load/unload/bootstrap/bootout/kickstart mutates system services. Refuse \u2014 surface the intent instead.",match:(e)=>e.head==="launchctl"&&typeof e.tokens[1]==="string"&&["load","unload","bootstrap","bootout","kickstart","enable","disable"].includes(e.tokens[1])},{rule:"defaults-write",action:"deny",message:"`defaults write` mutates macOS preferences. Refuse \u2014 surface the intent.",match:(e)=>e.head==="defaults"&&e.tokens[1]==="write"},{rule:"csrutil",action:"deny",message:"csrutil controls System Integrity Protection. Refuse.",match:(e)=>e.head==="csrutil"},{rule:"nvram",action:"deny",message:"nvram modifies firmware variables. Refuse.",match:(e)=>e.head==="nvram"},{rule:"diskutil-destructive",action:"deny",message:"diskutil eraseDisk / reformat / partitionDisk wipes disks. Refuse.",match:(e)=>e.head==="diskutil"&&typeof e.tokens[1]==="string"&&["eraseDisk","reformat","partitionDisk","eraseVolume","secureErase"].includes(e.tokens[1])},{rule:"tmutil-destructive",action:"deny",message:"tmutil delete / disablelocal touches Time Machine. Refuse.",match:(e)=>e.head==="tmutil"&&typeof e.tokens[1]==="string"&&["delete","disablelocal"].includes(e.tokens[1])},{rule:"osascript",action:"ask",message:"osascript runs arbitrary AppleScript and can do almost anything (move files, send emails, drive apps). Confirm with the user what you want done before running it.",match:(e)=>e.head==="osascript"},{rule:"topgrade",action:"deny",message:"topgrade upgrades everything (brew, mas, npm globals, rust, mise). System upgrades should be done manually.",match:(e)=>e.head==="topgrade"},{rule:"rsync-delete",action:"deny",message:"rsync --delete removes files at the destination. High blast radius \u2014 surface the intent instead.",match:(e)=>e.head==="rsync"&&e.flags.includes("--delete")},{rule:"softwareupdate-install",action:"deny",message:"`softwareupdate --install / -i / -d` triggers macOS system updates. Refuse \u2014 system updates should be done manually.",match:(e)=>e.head==="softwareupdate"&&e.flags.some((t)=>t==="--install"||t==="-i"||t==="-d"||t.startsWith("--download"))},{rule:"pmset-write",action:"deny",message:"`pmset` (with arguments) writes power-management settings. Read-only `pmset -g` is fine; mutations need the user.",match:(e)=>e.head==="pmset"&&e.tokens.length>1&&!e.tokens.includes("-g")&&!e.tokens.includes("-G")},{rule:"dscl-mutate",action:"deny",message:"`dscl . -create / -delete / -append / -change / -merge` modifies the local directory service (your user account, groups, etc.). Refuse.",match:(e)=>e.head==="dscl"&&e.tokens.some((t)=>["-create","-delete","-append","-change","-merge","-passwd"].includes(t))},{rule:"xattr-quarantine-bypass",action:"deny",message:"`xattr -d com.apple.quarantine` removes Gatekeeper's quarantine bit \u2014 the macOS protection against running untrusted binaries. Refuse.",match:(e)=>e.head==="xattr"&&e.tokens.includes("-d")&&e.tokens.some((t)=>t.includes("com.apple.quarantine"))},{rule:"spctl-disable",action:"deny",message:"`spctl --master-disable` / `--global-disable` disables Gatekeeper system-wide. Refuse.",match:(e)=>e.head==="spctl"&&e.flags.some((t)=>t==="--master-disable"||t==="--global-disable"||t==="--disable")},{rule:"kextload",action:"deny",message:"Loading a kernel extension (`kextload`, `kmutil load`) is a system-level mutation. Refuse \u2014 the user handles this manually.",match:(e)=>e.head==="kextload"||e.head==="kextunload"||e.head==="kmutil"&&(e.tokens[1]==="load"||e.tokens[1]==="unload")},{rule:"security-keychain-destructive",action:"deny",message:"`security delete-keychain / delete-generic-password / delete-internet-password / set-keychain-settings` mutates your Keychain (where CLIs store their secrets). Refuse \u2014 Keychain should be managed manually.",match:(e)=>e.head==="security"&&typeof e.tokens[1]==="string"&&["delete-keychain","delete-generic-password","delete-internet-password","delete-certificate","delete-identity","set-keychain-settings","unlock-keychain","lock-keychain","create-keychain"].includes(e.tokens[1])},{rule:"security-keychain-add-write",action:"ask",message:"`security add-generic-password / add-internet-password / add-certificate` writes to your Keychain. Other tools manage their own entries \u2014 confirm this is the right path before adding anything else manually.",match:(e)=>e.head==="security"&&typeof e.tokens[1]==="string"&&["add-generic-password","add-internet-password","add-certificate"].includes(e.tokens[1])},{rule:"systemsetup",action:"deny",message:"`systemsetup -set...` writes machine-wide settings (timezone, sleep, network time, restart-on-power-failure). Refuse.",match:(e)=>e.head==="systemsetup"&&e.tokens.some((t)=>t.startsWith("-set"))},{rule:"scutil-set",action:"deny",message:"`scutil --set` writes system configuration (computer name, hostname, LocalHostName). Refuse \u2014 read-only `scutil --get` is fine.",match:(e)=>e.head==="scutil"&&e.tokens.includes("--set")},{rule:"brew-mutation",action:"ask",message:"`brew install/uninstall/upgrade/untap` modifies the machine globally. Confirm before running.",match:(e)=>e.head==="brew"&&typeof e.tokens[1]==="string"&&["install","uninstall","upgrade","reinstall","untap","tap"].includes(e.tokens[1])},{rule:"mas-mutation",action:"ask",message:"`mas install/uninstall` changes installed Mac App Store apps. Confirm.",match:(e)=>e.head==="mas"&&typeof e.tokens[1]==="string"&&["install","uninstall","purchase"].includes(e.tokens[1])},{rule:"gh-destructive",action:"deny",message:"gh repo/release/issue delete is destructive on shared state. Refuse.",match:(e)=>e.head==="gh"&&typeof e.tokens[1]==="string"&&typeof e.tokens[2]==="string"&&["repo","release","issue","pr"].includes(e.tokens[1])&&["delete","destroy","remove"].includes(e.tokens[2])},{rule:"gh-api-mutation",action:"deny",message:"`gh api` with a mutating HTTP method can change shared GitHub state and bypass Git workflow protections. Refuse the inline mutation.",match:ds},{rule:"flyctl-destroy",action:"deny",message:"flyctl apps destroy / volumes destroy nukes deployed infra. Refuse.",match:(e)=>e.head==="flyctl"&&e.tokens.some((t)=>t==="destroy"||t==="delete")},{rule:"gcloud-delete",action:"deny",message:"`gcloud ... delete` affects cloud resources. Refuse.",match:(e)=>e.head==="gcloud"&&e.tokens.includes("delete")}],en=(e,t={})=>{let n=[];if(e.diagnostics.length>0){let r=[...new Set(e.diagnostics.map((i)=>i.message))],s=f("unsupported-shell-structure",`Tripwire cannot inspect this shell program safely: ${r.join(" ")}`);n.push(ne(e,s))}for(let r of e.invocations)if(ls(r)){let s=f("unsupported-shell-structure",`Tripwire cannot classify computed policy arguments to \`${r.head}\` safely.`);n.push(ne(e,s))}else{let s=us.find((i)=>i.match(r,e.source));if(s!==void 0){let i=s.action==="deny"?f(s.rule,s.message):b(s.rule,s.message);n.push(ne(e,Ft(i,t)))}}return n.length===0?u("bash-deny"):V(n)};var Ye=(e)=>e.protectedBranches??[],fs=/^(?<type>feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(?<scope>\([\w./\- ]+\))?!?:\s+\S/u,ps=new Set(["-C","-c","--git-dir","--work-tree","--namespace","--super-prefix"]),hs=new Set(["--bare","--paginate","-p","--no-pager","--no-replace-objects","--literal-pathspecs","--glob-pathspecs","--noglob-pathspecs","--icase-pathspecs","--no-optional-locks","--exec-path","--html-path","--man-path","--info-path"]),nn=new Set(["status","diff","diff-tree","log","show","blame","rev-parse","rev-list","ls-files","ls-tree","cat-file","reflog","describe","shortlog","whatchanged","archive","bundle","fsck","fetch","ls-remote","help","version","grep","name-rev","merge-base","symbolic-ref","check-ignore","count-objects","verify-commit","verify-tag"]),ms=(e)=>{if(e.head!=="git")return null;let t=e.tokens.slice(1),n=0;while(n<t.length){let r=t[n];if(r===void 0)break;if(ps.has(r))n+=2;else if(hs.has(r))n+=1;else if(r.startsWith("--git-dir=")||r.startsWith("--work-tree=")||r.startsWith("--namespace=")||r.startsWith("--super-prefix=")||r.startsWith("--exec-path="))n+=1;else if(r.startsWith("-"))n+=1;else return{subcommand:r,subcommandIndex:n+1,subArgs:t.slice(n+1)}}return null},gs=(e)=>{for(let t=0;t<e.length;t+=1){let n=e[t];if(n===void 0)break;if(n==="-m"||n==="--message")return e[t+1]??null;if(n.startsWith("--message="))return n.slice(10);if(/^-[a-zA-Z]*m[a-zA-Z]*$/u.test(n))return e[t+1]??null}return null},ys=(e,t)=>{let n=Ye(t);for(let r of e)for(let s of n)if(r===s||r.endsWith(`:${s}`)||r.endsWith(`/${s}`))return s;return null},bs=(e)=>e.filter((t)=>!t.startsWith("-")),Ss=(e)=>e.filter((t)=>t.startsWith("-")),y=(e,...t)=>{let n=new Set(e);return t.some((r)=>n.has(r))},ks=({subArgs:e,positional:t})=>{if(y(e,"--global","--system"))return f("git-config-global","Modifying global / system git config is off-limits \u2014 that is your personal identity. Read-only `git config --get` is fine.");if(!y(e,"--get","-l","--list","--get-all","--get-regexp")&&t.length>=2)return f("git-config-write","Local git config writes should be done explicitly. To read a value, use `git config --get <key>`.");return u("bash-git")},vs=({subArgs:e})=>{if(y(e,"--cached"))return u("bash-git");return b("git-rm","`git rm <path>` removes from the index AND the working tree. To untrack-only, use `git rm --cached <path>`. To delete the file separately, use `trash` / `rip`. Confirm intent.")},ws=({subArgs:e,positional:t})=>{if(y(e,"--staged","-S")&&!y(e,"--worktree","-W"))return u("bash-git");if(t.length>0)return f("git-restore-discard","`git restore <path>` discards uncommitted changes in the working tree. Refuse \u2014 `git diff <path>` to inspect first, or `git stash push <path>` to preserve.");return u("bash-git")},xs=({subArgs:e,positional:t})=>{if(y(e,"-b","-B"))return u("bash-git");if(y(e,"-f","--force"))return f("git-checkout-force","`git checkout -f` overwrites the working tree without preserving uncommitted changes. Refuse.");if(e.includes("--"))return f("git-checkout-discard","`git checkout -- <path>` discards uncommitted working-tree changes. Refuse \u2014 use `git stash push <path>` to preserve, or `git diff <path>` to inspect first.");let[n]=t;if(n!==void 0&&t.length===1&&(n==="."||n.startsWith("./")))return f("git-checkout-discard-all","`git checkout .` discards ALL uncommitted working-tree changes. Refuse \u2014 `git stash` to preserve, or `git diff` to inspect first.");return u("bash-git")},Es=({subArgs:e})=>{if(y(e,"-f","--force","--discard-changes"))return f("git-switch-force","`git switch -f / --discard-changes` throws away uncommitted working-tree changes. Refuse.");return u("bash-git")},Cs=({subArgs:e,flags:t,positional:n})=>{if(y(e,"--hard"))return f("git-reset-hard","`git reset --hard` discards all uncommitted changes AND moves HEAD. Refuse \u2014 describe the intent in chat. If undoing a published commit, `git revert <sha>` is safer.");if(y(e,"--keep"))return b("git-reset-keep","`git reset --keep` resets HEAD but preserves uncommitted local changes. Confirm intent.");if(n.length===0&&t.length===0)return u("bash-git");return b("git-reset-mixed","`git reset` moves HEAD. Confirm intent \u2014 if undoing a commit, `git revert` is usually safer.")},Rs=({subArgs:e,flags:t})=>{if(y(e,"-n","--dry-run")||t.some((n)=>/^-[a-zA-Z]*n/u.test(n)))return u("bash-git");if(t.some((n)=>/^-[a-zA-Z]*[df]/u.test(n)||n==="--force"))return f("git-clean-fd","`git clean -fd` deletes untracked files (often your in-progress work). Refuse \u2014 inspect with `git clean -dn` (dry run) first. If genuinely needed, append ` # tripwire-allow: <reason>`.");return u("bash-git")},Ps=({subArgs:e,positional:t,config:n})=>{if(y(e,"--abort","--quit","--continue","--skip","--edit-todo"))return u("bash-git");if(y(e,"-i","--interactive"))return f("git-rebase-interactive","`git rebase -i` rewrites history interactively. Refuse \u2014 too easy to lose commits in the agent loop. If this is genuinely required, do it manually outside the agent.");let[r]=t,s=Ye(n);if(r!==void 0&&s.includes(r))return b("git-rebase-onto-protected",`Rebasing onto \`${r}\` rewrites history of the current branch. \`git merge ${r}\` is usually safer. Confirm intent.`);return b("git-rebase","`git rebase` rewrites commit history. `git merge` is usually safer. Confirm intent.")},As=({subArgs:e})=>{if(y(e,"--abort","--quit","--continue","--skip"))return u("bash-git");return b("git-cherry-pick","`git cherry-pick` applies commits onto the current branch and can create conflicts. Confirm intent.")},Is=({subArgs:e})=>{if(y(e,"--abort","--continue","--quit"))return u("bash-git");return b("git-merge","`git merge <branch>` may create merge conflicts. Confirm intent.")},_s=({subArgs:e,config:t})=>{if(y(e,"--amend"))return f("git-commit-amend","`git commit --amend` rewrites the last commit. If it has been pushed, this causes upstream divergence. Refuse \u2014 surface the intent.");let n=gs(e),r=y(e,"-F","--file","-c","-C","--reuse-message","--reedit-message"),s=y(e,"--no-edit");if(n===null&&!r&&!s)return f("git-commit-no-message",'`git commit` without `-m "..."` opens an editor and hangs the agent. Use `git commit -m "<conventional message>"`.');if(n!==null&&t.enforceConventionalCommits===!0&&!fs.test(n))return f("git-commit-non-conventional",["Commit message must follow Conventional Commits format:"," `<type>(<scope>)?(!)?: <description>`","Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.","Examples:"," `fix(auth): handle expired token refresh`"," `feat: add bash-git rule`"," `chore: bump deps`",`Got: ${JSON.stringify(n)}`].join(`
7
+ `));if(y(e,"-a","--all")||e.some((i)=>/^-[a-zA-Z]*a[a-zA-Z]*$/u.test(i)))return b("git-commit-auto-stage","`git commit -a / --all` auto-stages every tracked change. Explicit `git add <files>` first is usually clearer about what is being committed. Confirm.");return u("bash-git")},Ds=({subArgs:e,flags:t,positional:n,config:r})=>{if(t.some((i)=>i==="--force"||i==="-f"||i.startsWith("--force-with-lease")))return f("git-force-push","Force push is forbidden. If a branch needs to be reset upstream, surface the intent \u2014 there is almost always a non-force path.");if(y(e,"--delete","--mirror")||e.some((i)=>i.startsWith(":")))return f("git-push-delete","Refusing to delete a remote branch via push. If genuinely needed, surface the intent.");let s=ys(n,r);if(s!==null)return f("git-push-protected",`Refusing to push directly to protected branch \`${s}\`. Open a PR instead: \`gh pr create\`.`);return u("bash-git")},Ts=({subArgs:e,flags:t,positional:n,config:r})=>{let s=t.find((i)=>i==="-D"||i==="-d"||i==="--delete"||/^-[a-zA-Z]*D/u.test(i)||/^-[a-zA-Z]*d/u.test(i));if(s!==void 0){let i=n,o=new Set(Ye(r)),a=i.find((l)=>o.has(l));if(a!==void 0)return f("git-branch-delete-protected",`Refusing to delete protected branch \`${a}\`.`);if(s==="-D"||s.includes("D"))return f("git-branch-force-delete",`\`git branch -D ${i.join(" ")}\` force-deletes branches even if unmerged (potential data loss). To delete a merged branch, use \`-d\`. To force, append \` # tripwire-allow: <reason>\`.`);return b("git-branch-delete",`\`git branch -d ${i.join(" ")}\` deletes a branch (merged-only check). Confirm intent.`)}if(y(e,"-m","-M","--move"))return b("git-branch-move","Renaming a branch can confuse pushed remotes. Confirm intent.");return u("bash-git")},Ws=({subArgs:e})=>{if(y(e,"-d","--delete"))return f("git-tag-delete","`git tag -d` deletes a tag. Refuse \u2014 surface intent.");return u("bash-git")},Ns=({subArgs:e})=>{let t=e[0]??"push";if(t==="drop"||t==="clear")return f("git-stash-drop",`\`git stash ${t}\` discards stashed work. Refuse \u2014 \`git stash list\` and \`git stash show\` to inspect first.`);return u("bash-git")},Os=({flags:e})=>{if(e.some((t)=>t.startsWith("--prune=")||t==="--aggressive"))return f("git-gc-prune","`git gc --prune=now` / `--aggressive` destroys reflog recovery options. Refuse.");return u("bash-git")},Fs=({subArgs:e})=>{let[t]=e;if(t!==void 0&&["add","prune","remove","rename","rm","set-branches","set-head","set-url","update"].includes(t))return b("git-remote-mutate",`\`git remote ${t}\` changes which remote you're pushing to. Confirm \u2014 accidentally pointing at the wrong remote is high blast-radius.`);return u("bash-git")},Hs=({subArgs:e})=>{if(y(e,"-f","--force"))return f("git-checkout-index-force","`git checkout-index --force` can overwrite working-tree files. Refuse.");return b("git-checkout-index","`git checkout-index` writes files from the index into the working tree. Confirm the target and intent.")},tn=({subcommand:e,subArgs:t})=>{let n=t[0]??"";if(["add","remove","rm","deinit","sync","set-url"].includes(n))return b("git-submodule-worktree-mutate",`\`git ${e} ${n}\` modifies repo structure. Confirm intent.`);return u("bash-git")},Vs=new Map([["config",ks],["add",()=>u("bash-git")],["mv",()=>u("bash-git")],["rm",vs],["restore",ws],["checkout",xs],["switch",Es],["reset",Cs],["clean",Rs],["rebase",Ps],["cherry-pick",As],["merge",Is],["commit",_s],["push",Ds],["branch",Ts],["tag",Ws],["stash",Ns],["filter-branch",({subcommand:e})=>f("git-filter",`\`git ${e}\` rewrites entire repo history. Refuse.`)],["filter-repo",({subcommand:e})=>f("git-filter",`\`git ${e}\` rewrites entire repo history. Refuse.`)],["gc",Os],["update-ref",()=>f("git-update-ref","`git update-ref` directly mutates refs and bypasses normal git operations. Refuse.")],["remote",Fs],["submodule",tn],["worktree",tn],["checkout-index",Hs],["write-tree",()=>u("bash-git")],["init",({subcommand:e})=>T(`git-${e}`,`\`git ${e}\` is allowed but unusual mid-session. Make sure this is what the user asked for.`)],["clone",({subcommand:e})=>T(`git-${e}`,`\`git ${e}\` is allowed but unusual mid-session. Make sure this is what the user asked for.`)]]),$s=(e,t)=>{let{subcommand:n,subArgs:r}=e,s=Ss(r),i=bs(r);if(nn.has(n)){if(n==="reflog"&&(r[0]==="expire"||y(r,"--expire")))return f("git-reflog-expire","`git reflog expire` destroys git's recovery history. Refuse \u2014 surface the intent.");if(n==="symbolic-ref"&&i.length>=2)return f("git-symbolic-ref-write","`git symbolic-ref <name> <ref>` rewrites a symbolic ref. Refuse.");return u("bash-git")}let o=Vs.get(n);if(o!==void 0)return o({subcommand:n,subArgs:r,flags:s,positional:i,config:t});return T("git-unknown-subcommand",`\`git ${n}\` is not classified by tripwire. Allowing \u2014 flag if this looks like history-rewriting or data-loss territory.`)},Ls=(e)=>{if(nn.has(e.subcommand))return!0;if(e.subcommand!=="remote")return!1;let t=e.subArgs.find((n)=>!n.startsWith("-"));return t===void 0||t==="get-url"||t==="show"},rn=(e,t)=>{for(let n of e.invocations){let r=ms(n);if(r!==null){if(n.words[r.subcommandIndex]?.kind==="dynamic")return f("git-dynamic-subcommand","Tripwire cannot classify a Git subcommand that is computed at runtime.");if((!Ls(r)||r.subcommand==="reflog"||r.subcommand==="symbolic-ref")&&n.words.slice(1).some((o)=>o.kind==="dynamic"))return f("git-dynamic-mutation",`Tripwire cannot prove that computed arguments to \`git ${r.subcommand}\` preserve Git safety policy.`);let i=$s(r,t);if(i!==null&&i.kind!=="allow")return i}}return u("bash-git")};var Bs=new Set(["curl","wget","wget2","aria2c","xh"]),Ms=new Set(["bash","sh","zsh","fish"]),Us=(e)=>{for(let t of e.invocations){let{pipeline:n}=t;if(Bs.has(t.head)&&n!==void 0){if(e.invocations.find((s)=>s.pipeline?.id===n.id&&s.pipeline.index===n.index+1&&Ms.has(s.head))!==void 0)return!0}}return!1},js=[{head:"cargo",subcommand:"install",rule:"cargo-install",message:"Confirm before `cargo install <crate>`: this builds and installs arbitrary code from crates.io into ~/.cargo/bin globally."},{head:"go",subcommand:"install",rule:"go-install",message:"Confirm before `go install`: this fetches and installs arbitrary Go code globally."},{head:"gem",subcommand:"install",rule:"gem-install",message:"Confirm before `gem install`: pulls arbitrary code from rubygems.org."}],sn=(e)=>{if(Us(e))return f("curl-pipe-shell","Piping `curl` / `wget` directly into a shell runs whatever the remote URL serves. Refuse \u2014 download to a file, inspect, then run if appropriate. If you genuinely need this, append ` # tripwire-allow: <reason>` (and explain to the user what you're running).");for(let t of e.invocations)for(let n of js)if(t.head===n.head&&t.tokens[1]===n.subcommand)return b(n.rule,n.message);return u("bash-network-install")};import{lstatSync as zs,readlinkSync as qs,realpathSync as Gs}from"fs";import re from"path";var Js=[{pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/u,rule:"env-file",message:".env files hold secrets that should never be sent to the model. Refuse to write or edit. If an example is needed, create .env.example with redacted placeholders."},{pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/u,rule:"dev-vars",message:".dev.vars holds Cloudflare/Wrangler secrets. Do not modify."},{pattern:/(?<prefix>^|\/)\.ssh\//u,rule:"ssh-dir",message:"Never write into ~/.ssh/. Refuse."},{pattern:/(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)(?<pub>\.pub)?$/u,rule:"ssh-key",message:"SSH key file. Refuse."},{pattern:/\.(?<ext>pem|key|p12|pfx)$/iu,rule:"private-key",message:"Private key file. Refuse to overwrite. If generating a new key, use a different filename and let the user review."},{pattern:/(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/iu,rule:"secrets-file",message:"Secrets file. Refuse."},{pattern:/(?<prefix>^|\/)\.aws\/credentials$/u,rule:"aws-credentials",message:"AWS credentials file. Refuse."},{pattern:/(?<prefix>^|\/)\.netrc$/u,rule:"netrc",message:".netrc holds host credentials. Refuse."}],on=(e)=>{try{return Gs(e)}catch{return null}},et=(e,t=new Set)=>{if(t.has(e))return e;t.add(e);let n=on(e);if(n!==null)return n;try{if(zs(e).isSymbolicLink()){let s=qs(e);return et(re.resolve(re.dirname(e),s),t)}}catch{}let r=re.dirname(e);if(r===e)return e;return re.join(et(r,t),re.basename(e))},de=(e,t,n)=>{let r=re.resolve(e),s=t==="write"?et(r):on(r),i=[e,r];if(s!==null&&!i.includes(s))i.push(s);for(let o of i)for(let a of n)if(a.pattern.test(o))return a;return null},ue=(e)=>{let t=de(e.file_path,"write",Js);if(t!==null)return f(t.rule,t.message);return u("path-protect")};var Ks=[{rule:"redirect-env",pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/u,message:"Refusing to write into a .env file via shell redirect / tee / cp / mv. .env files hold secrets \u2014 never overwrite from a tool call."},{rule:"redirect-dev-vars",pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/u,message:"Refusing to write into .dev.vars (Cloudflare/Wrangler secrets)."},{rule:"redirect-ssh",pattern:/(?<prefix>^|\/)\.ssh\//u,message:"Refusing to write into ~/.ssh/ via shell."},{rule:"redirect-key",pattern:/\.(?<ext>pem|key|p12|pfx)$/iu,message:"Refusing to overwrite a private-key-shaped file via shell."},{rule:"redirect-aws-credentials",pattern:/(?<prefix>^|\/)\.aws\/credentials$/u,message:"Refusing to write into ~/.aws/credentials via shell."},{rule:"redirect-netrc",pattern:/(?<prefix>^|\/)\.netrc$/u,message:"Refusing to write into ~/.netrc via shell."},{rule:"redirect-block-device",pattern:/^\/dev\/(?<type>sd|disk|nvme|rdisk)/iu,message:"Redirecting into a raw block device wipes the disk. Refuse."}],Zs=(e)=>{let t=de(e,"write",Ks);if(t!==null)return f(t.rule,t.message);return null},tt=(e)=>e.kind==="dynamic"||e.kind==="background-pid"?f("redirect-dynamic-target","Tripwire cannot prove that this computed write target avoids protected files."):Zs(e.value),an=(e)=>{for(let t of e.redirects)if(t.op===">"||t.op===">>"||t.op==="&>"||t.op==="&>>"){let n=tt(t.target);if(n!==null)return n}for(let t of e.invocations){let n=t.words.slice(1).filter((r)=>!r.value.startsWith("-"));if(t.head==="tee")for(let r of n){let s=tt(r);if(s!==null)return s}if(t.head==="cp"||t.head==="mv"){let r=n.at(-1);if(r!==void 0){let s=tt(r);if(s!==null)return s}}}return u("bash-redirect")};var Qs=(e,t)=>{let n=!1,r=[];for(let o of e.tokens.slice(1))if(!n&&o==="--")n=!0;else if(n||!o.startsWith("-")||o==="-")r.push(o);let s=t.relative??[],i=t.absolute??[];return r.filter((o)=>!ee(o,s,i))},Xs=(e,t)=>{if(!e.tokens.includes("-delete"))return null;let n=[];for(let o of e.tokens.slice(1)){if(o.startsWith("-"))break;n.push(o)}let r=n.length===0?["."]:n,s=t.relative??[],i=t.absolute??[];return r.filter((o)=>!ee(o,s,i))},ln=(e,t)=>{let n=[];for(let o of e.invocations)if(o.head==="rm"){let a=Qs(o,t);if(a.length>0)n.push({kind:"rm",targets:a})}else if(o.head==="find"){let a=Xs(o,t);if(a!==null&&a.length>0)n.push({kind:"find -delete",targets:a})}if(n.length===0)return u("bash-scoped-rm");let r=t.relative??[],s=t.absolute??[],i=n.map((o)=>` \u2022 ${o.kind} on: ${o.targets.map((a)=>JSON.stringify(a)).join(", ")}`).join(`
8
+ `);return f("destructive-outside-safe-paths",`Destructive deletion outside known-safe scopes is blocked. Use a recoverable deletion tool or limit the target to an ephemeral build, cache, state, or temporary directory:
9
+ ${Nt(r,s)}
16
10
 
17
11
  Flagged targets:
18
12
  ${i}
19
13
 
20
- If raw deletion is genuinely needed, append \` # tripwire-allow: <reason>\` to the command.`)};var _r=(e)=>e==="--extract"||e.startsWith("-")&&!e.startsWith("--")&&e.slice(1).includes("x"),Cr=(e)=>{for(let t=0;t<e.tokens.length;t+=1){let n=e.tokens[t];if(n===void 0)continue;if(n==="-C"||n==="--directory")return e.tokens[t+1]??null;if(n.startsWith("--directory="))return n.slice(12)}return null},Xe=(e)=>e==="/"||/^(?<home>~|\$HOME|\$\{HOME\})$/.test(e),Ye=(e,t)=>{if(k(t))return a("bash-tar-explosion");for(let n of e){if(n.head!=="tar")continue;let[,r]=n.tokens;if(!(n.flags.some(_r)||n.tokens.includes("--extract")||r!==void 0&&/^[a-zA-Z]+$/.test(r)&&r.includes("x")))continue;let s=Cr(n);if(s!==null&&Xe(s))return d("tar-extract-to-root",`tar -x with -C ${s} can overwrite arbitrary system files. Refuse \u2014 extract to a contained directory (e.g. ./tmp/extract) and inspect before moving anything elsewhere.`)}for(let n of e){if(n.head!=="unzip")continue;for(let r=0;r<n.tokens.length;r+=1)if(n.tokens[r]==="-d"){let o=n.tokens[r+1];if(o!==void 0&&Xe(o))return d("unzip-to-root",`unzip -d ${o} can overwrite arbitrary system files. Refuse \u2014 extract to a contained directory.`)}}return a("bash-tar-explosion")};var Pr="If this is intentional, append ` # tripwire-allow: <reason>` to the command.",Hr=new Map([["add","create"],["new","create"],["edit","update"],["set","update"],["rm","delete"],["del","delete"],["remove","delete"]]),Qe=(e)=>Hr.get(e)??e,et=(e)=>{let t=e.lastIndexOf("/");return t===-1?e:e.slice(t+1)},Tr=(e,t)=>e.some((n)=>n===t||n.startsWith(`${t}=`)),Dr=(e,t)=>{for(let n=0;n<e.length;n+=1){let r=e[n];if(r===void 0)continue;if(r===t)return e[n+1]??"";if(r.startsWith(`${t}=`))return r.slice(t.length+1)}return null},Ir=(e)=>{let t=[],n=e.tokens.slice(1);for(let r=0;r<n.length;r+=1){let o=n[r];if(o===void 0)continue;if(o.startsWith("-")){let s=n[r+1];if(!o.includes("=")&&s!==void 0&&!s.startsWith("-"))r+=1;continue}t.push(o)}return t},tt=(e,t)=>{let{pattern:n}=t,r=I(n);if(r.length===0)return!1;let[o]=r;if(o===void 0)return!1;let[s,...i]=o.tokens;if(s===void 0)return!1;for(let l of e){if(et(l.head)!==et(s))continue;if(i.length>0){let g=Ir(l);if(!i.every((w,C)=>g[C]!==void 0&&Qe(g[C])===Qe(w)))continue}if((t.requiresFlags??[]).some((g)=>!Tr(l.tokens,g)))continue;if(!(t.forbidsFlagValues??[]).every((g)=>{let p=Dr(l.tokens,g.flag);return p!==null&&g.values.includes(p)}))continue;if(i.length===0&&t.requiresFlags===void 0&&t.forbidsFlagValues===void 0)return!0;return!0}return!1},nt=(e,t,n,r)=>{if(k(t))return a("config-custom");for(let o of r)if(tt(e,o))return a("config-custom");for(let o of n)if(tt(e,o)){let s=o.message.includes("tripwire-allow")?o.message:`${o.message} ${Pr}`;return o.action==="ask"?y("config-custom",s):d("config-custom",s)}return a("config-custom")};import{readFileSync as Fr}from"fs";var rt=(e,t)=>{let n=new Set(e.split(`
14
+ If raw deletion is genuinely needed, append \` # tripwire-allow: <reason>\` to the command.`)};var Ys=(e)=>e==="--extract"||e.startsWith("-")&&!e.startsWith("--")&&e.slice(1).includes("x"),ei=(e)=>{for(let t=0;t<e.tokens.length;t+=1){let n=e.tokens[t];if(n!==void 0){if(n==="-C"||n==="--directory")return e.tokens[t+1]??null;if(n.startsWith("--directory="))return n.slice(12)}}return null},cn=(e)=>e==="/"||/^(?<home>~|\$HOME|\$\{HOME\})$/u.test(e),ti=(e)=>{let t=e.tokens.indexOf("-d");return t===-1?void 0:e.tokens[t+1]},dn=(e)=>{for(let t of e.invocations)if(t.head==="tar"){let[,n]=t.tokens,s=t.flags.some(Ys)||t.tokens.includes("--extract")||n!==void 0&&/^[a-zA-Z]+$/u.test(n)&&n.includes("x")?ei(t):null;if(s!==null&&cn(s))return f("tar-extract-to-root",`tar -x with -C ${s} can overwrite arbitrary system files. Refuse \u2014 extract to a contained directory (e.g. ./tmp/extract) and inspect before moving anything elsewhere.`)}for(let t of e.invocations)if(t.head==="unzip"){let n=ti(t);if(n!==void 0&&cn(n))return f("unzip-to-root",`unzip -d ${n} can overwrite arbitrary system files. Refuse \u2014 extract to a contained directory.`)}return u("bash-tar-explosion")};var ni="If this is intentional, append ` # tripwire-allow: <reason>` to the command.",ri=new Map([["add","create"],["new","create"],["edit","update"],["set","update"],["rm","delete"],["del","delete"],["remove","delete"]]),un=(e)=>ri.get(e)??e,fn=(e)=>{let t=e.lastIndexOf("/");return t===-1?e:e.slice(t+1)},si=(e,t)=>e.some((n)=>n===t||n.startsWith(`${t}=`)),ii=(e,t)=>{for(let n=0;n<e.length;n+=1){let r=e[n];if(r!==void 0){if(r===t)return e[n+1]??"";if(r.startsWith(`${t}=`))return r.slice(t.length+1)}}return null},oi=(e)=>{let t=[],n=e.tokens.slice(1);for(let r=0;r<n.length;r+=1){let s=n[r];if(s?.startsWith("-")===!0){let i=n[r+1];if(!s.includes("=")&&i!==void 0&&!i.startsWith("-"))r+=1}else if(s!==void 0)t.push(s)}return t},pn=(e,t)=>{let{pattern:n}=t,r=ae(n);if(r.diagnostics.length>0||r.invocations.length===0)return!1;let[s]=r.invocations;if(s===void 0)return!1;let[i,...o]=s.tokens;if(i===void 0)return!1;return e.invocations.some((a)=>{if(fn(a.head)!==fn(i))return!1;let l=oi(a),c=o.every((A,J)=>l[J]!==void 0&&un(l[J])===un(A)),h=(t.requiresFlags??[]).every((A)=>si(a.tokens,A)),x=(t.forbidsFlagValues??[]).every((A)=>{let J=ii(a.tokens,A.flag);return J!==null&&A.values.includes(J)});return c&&h&&x})},hn=(e,t,n)=>{for(let r of n)if(pn(e,r))return u("config-custom");for(let r of t)if(pn(e,r)){let s=r.message.includes("tripwire-allow")?r.message:`${r.message} ${ni}`;return r.action==="ask"?b("config-custom",s):f("config-custom",s)}return u("config-custom")};import{lstatSync as Li,realpathSync as Bi}from"fs";import q from"path";import vi from"path";import{parser as mn}from"@lezer/javascript";import{parser as ai}from"@lezer/python";var li={python:ai.configure({strict:!0}),javascript:mn.configure({strict:!0}),typescript:mn.configure({dialect:"ts",strict:!0})},ci=65536,di=12000,ui=80;class B extends Error{name="CodeInspectionError"}var d=(e)=>{throw new B(e)},m=(e)=>{let t=[];for(let n=e.firstChild;n!==null;n=n.nextSibling)t.push(n);return t},gn=(e,t)=>{if(t.length>ci)throw new B("Code exceeds the source size limit.");let r=li[e].startParse(t),s=performance.now()+40,i=r.advance();while(i===null){if(performance.now()>s)throw new B("Code exceeds the parsing time budget.");i=r.advance()}let o=0,a=[{node:i.topNode,depth:0}];while(a.length>0){let l=a.pop();if(l===void 0)break;if(o+=1,o>di||l.depth>ui||performance.now()>s)throw new B("Code exceeds the tree inspection budget.");if(l.node.type.isError)throw new B("Code contains invalid or incomplete syntax.");for(let c of m(l.node))a.push({node:c,depth:l.depth+1})}return i.topNode},we=(e,t="javascript")=>{let n=t==="python"&&/^[rR]["']/u.test(e),r=n?e.slice(1):e,[s]=r;if(s!=='"'&&s!=="'")throw new B("Unsupported string literal.");let i=t==="python"&&r.startsWith(s.repeat(3))?s.repeat(3):s;if(!r.endsWith(i))return d("Incomplete string literal.");let o=r.slice(i.length,-i.length);return n?o:fi(o)},fi=(e)=>{let t={n:`
15
+ `,r:"\r",t:"\t",b:"\b",f:"\f",v:"\v","\\":"\\","'":"'",'"':'"'},n="";for(let r=0;r<e.length;r+=1){let s=e[r];if(s==="\\"){r+=1;let i=e[r];if(i===void 0)return d("Incomplete escape.");if(t[i]!==void 0)n+=t[i];else if(i==="x"||i==="u"){let o=i==="x"?2:4,a=e.slice(r+1,r+1+o);if(a.length!==o||!/^[0-9a-f]+$/iu.test(a))return d("Unsupported Unicode escape.");n+=String.fromCodePoint(Number.parseInt(a,16)),r+=o}else return d("Unsupported string escape.")}else n+=s}return n};import pi from"path";var F={kind:"unknown"},_=(e)=>({kind:"symbol",name:e}),se=(e)=>{if(e.length>65536)return d("A resolved string exceeds its size limit.");return{kind:"string",value:e}},C=(e)=>{if(e?.kind==="string")return e.value;if(e?.kind==="path")return e.path;return d("An operation has an unresolved path or argument.")},hi=new Set(["os","os.path","shutil","pathlib","subprocess","json","math","sys","re"]),mi=new Set(["fs","fs/promises","path","child_process"]),gi=["print","len","str","int","float","abs","round","open","sum","sorted","list","bool","min","max","any","all"],yi=["console","require","JSON","Deno"],yn=(e)=>new Map((e==="python"?gi:yi).map((t)=>[t,_(t)])),ie=(e,t)=>{let n=t!=="python"&&e.startsWith("node:")?e.slice(5):e;if(!(t==="python"?hi:mi).has(n))return d(`Module ${JSON.stringify(e)} can execute code that was not inspected.`);return n==="fs/promises"?"fs":n},fe=(e)=>{let t="";for(let n of e)t=n.startsWith("/")?n:pi.posix.join(t,n);return t};var k={kind:"data"},nt=(e)=>({kind:"text",path:e,preservesNonempty:!0}),z=(e)=>{let t=[e],n=new Set;while(t.length>0){let r=t.pop();if(r!==void 0&&!n.has(r)){if(n.add(r),n.size>12000)return d("Data exceeds the value inspection budget.");switch(r.kind){case"string":case"number":case"text":case"data":break;case"list":{t.push(...r.items);break}case"object":{t.push(...r.entries.values());break}case"file":case"path":case"method":case"symbol":case"unknown":return!1;default:return!1}}}return!0},R=(e)=>{if(!e.every((t)=>z(t)))d("Only inert data can be passed to a data operation.")},rt=(e,t)=>{if(t?.kind==="string"&&t.value.length>0)return"write";if(t?.kind==="text"&&t.path===e&&t.preservesNonempty)return"write";return"truncate"},bi=(e,t,n)=>{let[r,s]=t;if(t.length!==2||r?.kind!=="string"||s?.kind!=="string")return d("Text replacement needs two literal strings.");let i=s.value.length>0&&(n==="python"||!s.value.includes("$"));return e.kind==="text"?{...e,preservesNonempty:e.preservesNonempty&&i}:{kind:"text",path:null,preservesNonempty:!1}},bn=(e,t,n,r)=>{if(R(n),e.kind==="string"||e.kind==="text"){if(t==="replace"||t==="replaceAll")return bi(e,n,r);if(["strip","trim","lower","upper","toLowerCase","toUpperCase","slice","substring"].includes(t))return{kind:"text",path:null,preservesNonempty:!1};if(["split","splitlines","startswith","endswith","startsWith","endsWith","includes","find","index","indexOf"].includes(t))return k}if(r==="python"&&["get","keys","values","items","count","index"].includes(t)&&z(e))return k;return d(`Unsupported data method: ${t}.`)},Si=(e)=>{let[t]=e,n=t?.kind==="file"||t?.kind==="symbol"&&t.name==="sys.stdin";if(e.length!==1||!n)return d("JSON load requires a known file or stdin without callbacks.");return k},ki=(e)=>{let[t,n,r]=e;if(e.length!==3||t?.kind!=="string"||n?.kind!=="string"||r===void 0)return d("Regex substitution needs literal pattern/replacement and one input.");R([r]);let s=n.value.length>0&&!n.value.includes("\\");return r.kind==="text"?{...r,preservesNonempty:r.preservesNonempty&&s}:{kind:"text",path:null,preservesNonempty:!1}},Sn=(e,t)=>{if(e==="re.sub")return ki(t);if(e==="json.load")return Si(t);if(["json.loads","JSON.parse","json.dumps","JSON.stringify","str"].includes(e)){if(R(t),t.length!==1)return d("JSON callbacks and serialization options require review.");return["json.loads","JSON.parse"].includes(e)?k:{kind:"text",path:null,preservesNonempty:!1}}if(["len","int","float","abs","round","sum","sorted","list","bool","min","max","any","all"].includes(e))return R(t),k;if(["print","console.log","console.info","console.warn","console.error"].includes(e))return R(t),F;if(e==="sys.stdin.read")return R(t),{kind:"text",path:null,preservesNonempty:!1};return null};var wi=new Map([...["os.remove","os.unlink","os.rmdir","shutil.rmtree","fs.rm","fs.rmSync","fs.unlink","fs.unlinkSync","fs.rmdir","fs.rmdirSync","Deno.remove","Deno.removeSync"].map((e)=>[e,"delete"]),...["fs.readFile","fs.readFileSync","Deno.readTextFile","Deno.readTextFileSync"].map((e)=>[e,"read"]),...["fs.writeFile","fs.writeFileSync","fs.appendFile","fs.appendFileSync","Deno.writeTextFile","Deno.writeTextFileSync"].map((e)=>[e,"write"]),...["os.truncate","fs.truncate","fs.truncateSync"].map((e)=>[e,"truncate"])]),xi=new Set(["subprocess.run","subprocess.call","subprocess.check_call","subprocess.check_output","subprocess.Popen","child_process.execFile","child_process.execFileSync","child_process.spawn","child_process.spawnSync"]),Ei=(e,t)=>{if(!e.startsWith("fs."))return;if(t.length>2)return d("Read callbacks are not inspected.");let[,n]=t;if(n===void 0||n.kind==="string")return;if(n.kind!=="object")return d("Read options are unresolved.");for(let[r,s]of n.entries){if(r==="flag"&&C(s)!=="r")return d("A file read flag can truncate or write the file.");if(r!=="flag"&&r!=="encoding")return d("Unsupported read option.");C(s)}},Ci=(e,t)=>{if(!e.startsWith("fs."))return;let n=t.at(2);if(n===void 0&&t.length<=2)return;if(t.length===3&&n?.kind==="string"&&n.value.replace("-","")==="utf8")return;return d("Write encodings, flags, and callbacks require explicit review.")},Ri=(e,t)=>{let[n,r]=t;if(e.startsWith("subprocess.")){if(t.length!==1||n?.kind!=="list")return d("Subprocess options or shell execution require review.");if(n.items.length===0)return d("Empty subprocess argument list.");return n.items.map((s)=>C(s))}if(t.length>2||r!==void 0&&r.kind!=="list")return d("Subprocess options require review.");return[C(n),...r?.kind==="list"?r.items.map((s)=>C(s)):[]]},Pi=(e,t,n,r)=>{let i=(e.kind==="path"?{unlink:"delete",rmdir:"delete",read_text:"read",read_bytes:"read",write_text:"write",write_bytes:"write"}:{read:"read",write:"write"})[t];if(i===void 0)return d(`Unsupported ${e.kind} method: ${t}.`);if(i==="read"&&n.length>0)return d("File read encoding and offset options require review.");if(i==="write"&&n.length!==1)return d("File write encoding and callback options require review.");let o=i==="write"?rt(e.path,n[0]):i;return r.operations.push({kind:o,path:e.path,range:r.range}),i==="read"?nt(e.path):F},Ai=(e,t)=>{if(e.length>2)return d("File opener options are not inspected.");let n=e[1]===void 0?"r":C(e[1]),s={r:"read",rb:"read",rt:"read",w:"truncate",wb:"truncate",wt:"truncate",a:"write",ab:"write",at:"write"}[n];if(s===void 0)return d("Unsupported file open mode.");let i=C(e[0]);return t.operations.push({kind:s,path:i,range:t.range}),{kind:"file",path:i}},kn=(e,t,n)=>{if(e.kind==="method")return e.receiver.kind==="path"||e.receiver.kind==="file"?Pi(e.receiver,e.name,t,n):bn(e.receiver,e.name,t,n.language);if(e.kind!=="symbol")return d("Dynamic call target.");let{name:r}=e,s=Sn(r,t);if(s!==null)return s;if(r==="require")return _(ie(C(t[0]),n.language));if(r==="pathlib.Path"||r==="pathlib.PosixPath")return{kind:"path",path:fe(t.map((o)=>C(o)))};if(r==="os.path.join")return se(fe(t.map((o)=>C(o))));if(r==="path.join")return se(vi.posix.join(...t.map((o)=>C(o))));if(r==="open")return Ai(t,n);let i=wi.get(r);if(i!==void 0)return Ii(r,i,t,n);else if(xi.has(r))n.operations.push({kind:"process",argv:Ri(r,t),range:n.range});else return d(`Call ${JSON.stringify(r)} has uninspected effects.`);return F},Ii=(e,t,n,r)=>{if(t==="read")Ei(e,n);if(t==="write")Ci(e,n);let s=C(n[0]),i=t==="write"?rt(s,n[1]):t;if(r.operations.push({kind:i,path:s,range:r.range}),t==="read"){let[,o]=n;return e.startsWith("Deno.")||o?.kind==="string"&&o.value.toLowerCase().replace("-","")==="utf8"?nt(s):k}return F};var _i=(e,t,n)=>{let r=new Map;for(let s of m(e).filter((i)=>!["{","}",","].includes(i.name))){let i=m(s),[o,a,l]=i;if(s.name!=="Property"||o?.name!=="PropertyDefinition"||a?.name!==":"||l===void 0||i.length!==3)return d("Computed properties, accessors, and object spread are not inspected.");r.set(n(o),t(l))}return{kind:"object",entries:r}},Di=(e,t)=>{let n=m(e).filter((s)=>!["{","}",","].includes(s.name)),r=new Map;for(let s=0;s<n.length;s+=3){let[i,o,a]=n.slice(s,s+3);if(i===void 0||o?.name!==":"||a===void 0)return d("Unsupported dictionary entry.");let l=t(i);if(l.kind!=="string")return d("Dictionary keys must be literal strings.");r.set(l.value,t(a))}return{kind:"object",entries:r}},Ti=(e,t)=>{for(let n of m(e)){if(n.name!=="FormatReplacement")return d("Unsupported formatted string component.");let r=m(n).filter((s)=>!["{","}"].includes(s.name));if(r.length!==1||r[0]===void 0)return d("Format conversions and specifications require review.");R([t(r[0])])}return{kind:"text",path:null,preservesNonempty:!1}},vn=(e,t,n)=>{switch(e.name){case"ObjectExpression":return _i(e,t,n);case"DictionaryExpression":return Di(e,t);case"FormatString":return Ti(e,t);default:return null}};var wn=(e,t)=>{e.clear();for(let[n,r]of t)e.set(n,r)},xn=(e,t)=>{let n=new Set(t.flatMap((r)=>[...r.keys()]));e.clear();for(let r of n){let s=t[0]?.get(r);e.set(r,s!==void 0&&t.every((i)=>i.get(r)===s)?s:F)}},En=(e,t)=>{let n=new Map(t.bindings),r=[n];for(let s of m(e).filter((i)=>!["if","elif","else","(",")"].includes(i.name)))if(wn(t.bindings,n),s.name==="Body"||s.name==="Block")t.statement(s),r.push(new Map(t.bindings));else R([t.evaluate(s)]);xn(t.bindings,r)},Cn=(e,t)=>{let n=m(e);if(["AssignStatement","ImportStatement","ForStatement","VariableDeclaration","WithStatement"].includes(e.name)){let r=n.findIndex((s)=>["AssignOp","Equals","in"].includes(s.name));for(let s of r===-1?n:n.slice(0,r))if(s.name==="VariableName"||s.name==="VariableDefinition")t.bindings.set(t.text(s),F)}for(let r of n)Cn(r,t)},st=(e,t,n,r,s)=>{if(e===void 0||!["VariableName","VariableDefinition"].includes(e.name)||t===void 0)return d("Only simple iteration bindings are inspected.");let i=n.evaluate(t),o;if(i.kind==="list")o=i.items;else if(i.kind==="data")o=[k];else return d("Iteration needs bounded literals or inert JSON data.");if(o.length>128)return d("Iteration needs bounded literals or inert JSON data.");let l=[new Map(n.bindings)];if(i.kind==="data"&&s!==void 0)Cn(s,n);for(let c of o.length===0?[F]:o)n.bindings.set(n.text(e),c),r(),l.push(new Map(n.bindings));xn(n.bindings,l)},Rn=(e,t)=>{let n=m(e),r=n.find((o)=>o.name==="ForOfSpec");if(r!==void 0){let o=m(r),[,a,l,c,h]=o,x=n.at(2);if(o.length!==6||!["const","let"].includes(a?.name??"")||c?.name!=="of"||x?.name!=="Block")return d("Unsupported JavaScript iteration form.");st(l,h,t,()=>{t.statement(x)},x);return}let s=n.findIndex((o)=>o.name==="in"),i=n[s+2];if(s!==2||i?.name!=="Body"||n.length!==5)return d("Unsupported loop shape.");st(n[s-1],n[s+1],t,()=>{t.statement(i)},i)},it=(e,t)=>{let n=e.findIndex((i)=>i.name==="for"),r=e[n-1];if(n!==1||r===void 0||e[n+2]?.name!=="in"||![5,7].includes(e.length))return d("Only single-generator comprehensions are inspected.");let s=new Map(t.bindings);return st(e[n+1],e[n+3],t,()=>{if(e.length===7){let[,i]=e.slice(5),o=i??d("Missing comprehension filter.");if(e[5]?.name!=="if")d("Unsupported comprehension filter.");R([t.evaluate(o)])}R([t.evaluate(r)])}),wn(t.bindings,s),k};var Wi=(e,t)=>{let n=t===null?ie(e,"python"):`${t}.${e}`;return{qualified:n,bound:t===null?n.split(".")[0]??n:n}},Pn=(e,t)=>{let[n,r]=e;if(r===void 0)return d("Missing Python import.");let s=n?.name==="from"?ie(t(r),"python"):null,i=new Map,o=s===null?1:e.findIndex((a)=>a.name==="import")+1;while(o<e.length){let a=e[o];if(a?.name!=="VariableName"&&a?.name!=="MemberExpression")return d("Unsupported Python import list.");let l=Wi(t(a),s);o+=1;let c=t(a).split(".")[0]??"",{bound:h}=l;if(e[o]?.name==="as"){let x=e[o+1];if(x?.name!=="VariableName")return d("Invalid import alias.");c=t(x),h=l.qualified,o+=2}if(i.set(c,_(h)),o<e.length&&e[o]?.name!==",")return d("Unsupported import separator.");o+=1}return i},An=(e,t,n)=>{let r=e.find((l)=>l.name==="String");if(r===void 0)return d("Import source is unresolved.");let s=ie(we(t(r)),n),i=e.find((l)=>l.name==="ImportGroup");if(i===void 0){let l=e.find((c)=>c.name==="VariableDefinition");if(l===void 0)return d("Unsupported JavaScript import.");return new Map([[t(l),_(s)]])}if(e.some((l)=>l.name==="VariableDefinition"))return d("Combined default and named import requires review.");let o=m(i).filter((l)=>!["{","}",","].includes(l.name)),a=new Map;for(let l=0;l<o.length;l+=1){let c=o[l];if(c===void 0)break;let h=o[l+1]?.name==="as"?o[l+2]:c;if(h===void 0)return d("Import alias is unresolved.");if(a.set(t(h),_(`${s}.${t(c)}`)),h!==c)l+=2}return a};var In=(e,t)=>{if(!z(t))return d("Index has uninspected effects.");if(e.kind==="data"||e.kind==="text"||e.kind==="string")return k;if(e.kind==="list"&&t.kind==="number")return e.items[t.value]??k;if(e.kind==="object"&&t.kind==="string")return e.entries.get(t.value)??k;return d("Computed property access is not inspected.")},_n=(e,t,n)=>{if(t.startsWith("_")||t==="constructor"||t==="prototype")return d("Runtime reflection is not inspected.");if(e.kind==="symbol")return _(`${e.name}.${t}`);if(e.kind==="path"||e.kind==="file")return{kind:"method",receiver:e,name:t};if(e.kind==="object"&&n!=="python")return e.entries.get(t)??k;if(z(e)){if(e.kind==="data"&&n!=="python")return k;return{kind:"method",receiver:e,name:t}}return d("Unknown receiver may execute a property getter.")};var Dn=new Set(["Comment","LineComment","BlockComment",";"]);class Tn{operations=[];#n;#e;#i;#l=0;#d=0;constructor(e,t){this.#e=e,this.#i=t,this.#n=yn(t)}#t(e){return this.#e.slice(e.from,e.to)}inspect(e){for(let t of m(e))this.#u(t)}#u(e){if(this.#f(),Dn.has(e.name))return;let t=m(e).filter((n)=>!Dn.has(n.name));switch(e.name){case"Body":case"Block":case"StatementGroup":{for(let n of t)if(![":","{","}"].includes(n.name))this.#u(n);return}case"ExpressionStatement":{for(let n of t)this.#r(n);return}case"AssignStatement":case"VariableDeclaration":{this.#p(t);return}case"ImportStatement":case"ImportDeclaration":{let n=(s)=>this.#t(s),r=e.name==="ImportStatement"?Pn(t,n):An(t,n,this.#i);for(let[s,i]of r)this.#n.set(s,i);return}case"ForStatement":{Rn(e,this.#g());return}case"IfStatement":{En(e,this.#g());return}case"AssertStatement":{R(t.filter((n)=>!["assert",","].includes(n.name)).map((n)=>this.#r(n)));return}case"WithStatement":{this.#m(t);return}default:d(`Unsupported executable syntax: ${e.name}.`)}}#p(e){let t=e.findIndex((o)=>o.name==="AssignOp"||o.name==="Equals"),n=e.find((o)=>["VariableName","VariableDefinition","ObjectPattern"].includes(o.name)),r=e[t],s=e[t+1];if(r===void 0||n===void 0||s===void 0||e.length!==t+2)return d("Unsupported binding or assignment.");if(this.#t(r)!=="=")return d("Compound assignment is not inspected.");if(e.slice(0,t).some((o)=>["MemberExpression","ArrayPattern"].includes(o.name)))return d("Destructuring or object mutation is not inspected.");let i=this.#r(s);if(n.name==="ObjectPattern")this.#h(n,i);else this.#n.set(this.#t(n),i)}#m(e){let[,t,n,r,s]=e;if(e.length!==5||t===void 0||n?.name!=="as"||r?.name!=="VariableName"||s?.name!=="Body")return d("Only a single known file context is inspected.");let i=this.#r(t);if(i.kind!=="file")return d("Context manager callbacks require review.");this.#n.set(this.#t(r),i),this.#u(s)}#h(e,t){if(t.kind!=="symbol")return d("Only named module members can be destructured.");for(let n of m(e).filter((r)=>!["{","}",","].includes(r.name))){let r=m(n),[s,i,o]=r;if(n.name!=="PatternProperty"||s?.name!=="PropertyName")return d("Computed or rest destructuring is not inspected.");if(r.length===1)this.#n.set(this.#t(s),_(`${t.name}.${this.#t(s)}`));else if(r.length===3&&i?.name===":"&&o?.name==="VariableDefinition")this.#n.set(this.#t(o),_(`${t.name}.${this.#t(s)}`));else return d("Destructuring defaults are not inspected.")}}#r(e){this.#f();let t=this.#k(e);if(t.kind==="string")this.#l+=t.value.length;if(t.kind==="path"||t.kind==="file")this.#l+=t.path.length;if(this.#l>1048576)return d("Code exceeds the resolved value budget.");return t}#f(){if(this.#d+=1,this.#d>12000)d("Code exceeds the evaluation step budget.")}#g(){return{bindings:this.#n,text:(e)=>this.#t(e),evaluate:(e)=>this.#r(e),statement:(e)=>{this.#u(e)}}}#k(e){let t=vn(e,(n)=>this.#r(n),(n)=>this.#t(n));if(t!==null)return t;switch(e.name){case"String":return se(we(this.#t(e),this.#i));case"Number":return{kind:"number",value:Number(this.#t(e))};case"Boolean":case"BooleanLiteral":case"None":case"Null":return k;case"VariableName":return this.#n.get(this.#t(e))??d("Unresolved variable or persistent runtime state.");case"ParenthesizedExpression":case"AwaitExpression":{let n=m(e).find((r)=>!["(",")","await"].includes(r.name));return n===void 0?d("Missing expression."):this.#r(n)}case"ArrayExpression":case"Array":case"TupleExpression":return{kind:"list",items:m(e).filter((n)=>!["[","]","(",")",","].includes(n.name)).map((n)=>this.#r(n))};case"ArrayComprehensionExpression":return it(m(e).filter((n)=>!["[","]"].includes(n.name)),this.#g());case"BinaryExpression":return this.#o(e);case"MemberExpression":return this.#y(e);case"CallExpression":return this.#v(e);default:return d(`Unsupported expression: ${e.name}.`)}}#o(e){let[t,n,r]=m(e);if(t===void 0||n===void 0||r===void 0)return d("Incomplete binary expression.");let s=this.#r(t),i=this.#r(r),o=this.#t(n);if(s.kind==="string"&&i.kind==="string"&&o==="+")return se(s.value+i.value);if(s.kind==="path"&&o==="/")return{kind:"path",path:fe([s.path,C(i)])};if(s.kind==="number"&&i.kind==="number"&&["+","-","*","/","%","**","==","===","<",">"].includes(o))return k;if(z(s)&&z(i))return k;return d("Dynamic operators are not inspected.")}#y(e){let t=m(e),[n,r,s]=t;if(n===void 0||s===void 0||r===void 0)return d("Computed property access is not inspected.");let i=this.#r(n);if(this.#i==="python"&&this.#t(r)==="["&&t.some((o)=>o.name===":")){let o=t.slice(2,-1).filter((a)=>a.name!==":");return R([i,...o.map((a)=>this.#r(a))]),k}if(this.#t(r)==="["&&t.length===4)return In(i,this.#r(s));if(this.#t(r)!=="."||s.name!=="PropertyName"||t.length!==3)return d("Unsupported property access.");return _n(i,this.#t(s),this.#i)}#v(e){let[t,n]=m(e);if(t===void 0||n?.name!=="ArgList")return d("Unsupported call syntax.");let r=this.#r(t),s=m(n).filter((o)=>!["(",")",","].includes(o.name)),i=s.some((o)=>o.name==="for")?[it(s,this.#g())]:s.map((o)=>this.#r(o));if(i.length>128)return d("Call exceeds the argument count limit.");return kn(r,i,{language:this.#i,range:{start:e.from,end:e.to},operations:this.operations})}}var Wn=(e,t)=>{let n=new Tn(t,e);try{return n.inspect(gn(e,t)),{operations:n.operations,gap:null}}catch(r){return{operations:n.operations,gap:r instanceof B?r.message:"The code inspector failed."}}};import pe from"path";var Ni=new Set(["--no-sync","--locked","--frozen","--offline","--no-project","--no-config","--no-python-downloads","--quiet","-q","--verbose","-v"]),Nn=(e)=>{let t=e.words.slice(1);if(t.some((o)=>o.kind!=="literal"))return{kind:"blocked",reason:"The uv command contains unresolved arguments."};let n=0,r=!1,s=()=>{while(n<t.length){let o=t[n]?.value??"";if(Ni.has(o))r||=o==="--no-sync",n+=1;else if(o==="--python"||o==="-p"||o.startsWith("--python=")){let a=o.startsWith("--python="),l=a?o.slice(9):t[n+1]?.value??"";if(!/^[23](?:\.\d+){0,2}t?$/u.test(l))return!1;n+=a?1:2}else break}return!0};if(!s())return{kind:"blocked",reason:"The uv Python selector must be a literal version."};if(t[n]?.value!=="run")return e.tokens.includes("run")?{kind:"blocked",reason:"The uv run options require review."}:{kind:"irrelevant"};if(n+=1,!s())return{kind:"blocked",reason:"The uv Python selector must be a literal version."};if(t[n]?.value==="--")n+=1;let i=t[n]?.value;if(i===void 0||i.startsWith("-")&&i!=="-")return{kind:"blocked",reason:"The uv run command or options require review."};return{kind:"command",argv:t.slice(n).map((o)=>o.value),noSync:r}};var xe={kind:"irrelevant"},H=(e)=>({kind:"blocked",reason:e}),at=(e)=>{let t=pe.posix.basename(e);if(/^(?:python|pypy)(?:[23](?:\.\d+)*t?)?$/u.test(t))return"python";if(["node","nodejs"].includes(t))return"javascript";if(["bun","deno","tsx","ts-node"].includes(t))return"typescript";return null},Oi=new Set(["poetry","pipenv","pipx","npx","bunx","npm","pnpm","yarn"]),Fi=new Set(["--script","--module","-m","-c","-e","--eval","--call"]),Hi=(e)=>{let t=pe.posix.basename(e.head);if(!Oi.has(t))return!1;return e.words.slice(1).some((n)=>at(n.value)!==null||Fi.has(n.value.split("=")[0]??"")||/\.(?:py|pyw|mjs|cjs|js|jsx|ts|tsx)$/u.test(n.value))},Vi=(e)=>{let t=Nn(e);if(t.kind!=="command")return t;let n=t.argv[0]??"",r=n==="-"?"python":at(n);if(r!==null){if(!t.noSync)return H("Inline code through uv requires --no-sync and an existing trusted environment.");if(n==="-")return t.argv.length===1?ot(e,r):H("Trailing uv stdin arguments require review.");return On(t.argv.slice(1),r,pe.posix.basename(n))??ot(e,r)}if(/\.(?:py|pyw|mjs|cjs|js|jsx|ts|tsx)$/u.test(n))return H("Script files require explicit review.");return{kind:"command",argv:t.argv}},ot=(e,t)=>{let n=e.redirects.filter((s)=>["<<","<<<","<","<&"].includes(s.op));if(n.length!==1)return H("Interpreter input is absent, piped, or redirected more than once.");let[r]=n;if(r?.op==="<<"&&r.heredoc!==void 0){if(!r.heredoc.quoted&&/[$`\\]/u.test(r.heredoc.content))return H("Unquoted interpreter heredoc can change during shell expansion.");return{kind:"source",language:t,source:r.heredoc.content}}if(r?.op==="<<<"&&r.target.kind==="literal")return{kind:"source",language:t,source:r.target.value};return H("Interpreter input bytes are not available for inspection.")},On=(e,t,n)=>{let r=new Set(t==="python"?["-I","-S","-E","-s","-B","-u","-q"]:["--input-type=module","--input-type=commonjs","--no-warnings"]),s=0;while(s<e.length&&r.has(e[s]??""))s+=1;let i=e[s];if(i===void 0||i==="-"&&s===e.length-1)return null;let o=t==="python"?["-c"]:["-e","--eval","-p","--print"];if(n==="deno")o.push("eval");if(o.includes(i)){let c=e[s+1];if(c===void 0||e.length!==s+2)return H("Interpreter source or trailing arguments are not fully inspected.");return{kind:"source",language:t,source:c}}let l=(t==="python"?["-c"]:["-e","-p","--eval=","--print="]).find((c)=>i.startsWith(c));if(l!==void 0&&e.length===s+1)return{kind:"source",language:t,source:i.slice(l.length)};return H("Interpreter flags, script files, modules, and interactive sessions require explicit review.")},Fn=(e)=>{if(Hi(e))return H("A package launcher can change the interpreter or load project startup code. Use a directly inspected interpreter.");if(pe.posix.basename(e.head)==="uv")return Vi(e);let t=at(e.head);if(t===null)return xe;let n=e.words.slice(1),r=pe.posix.basename(e.head);if(r==="bun"&&n[0]?.kind==="literal"&&["add","install","remove","update","outdated","pm","build","test"].includes(n[0].value))return xe;if(n.some((s)=>s.kind!=="literal"))return H("Interpreter arguments contain runtime substitutions.");if(r==="bun"&&n[0]?.value==="run"&&/^(?:check|test|typecheck|lint|format|build|verify)(?::[\w-]+)*$/u.test(n[1]?.value??""))return xe;if(n.length===1&&["--version","-V","--help","-h"].includes(n[0]?.value??""))return xe;return On(n.map((s)=>s.value),t,r)??ot(e,t)};var $i=[{rule:"read-env",pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/u,message:".env files hold secrets that should never enter the model context. Refuse to read. If the goal is documenting required env vars, look at .env.example or describe the schema from memory."},{rule:"read-dev-vars",pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/u,message:"Refuse to read .dev.vars (Cloudflare/Wrangler secrets)."},{rule:"read-ssh",pattern:/(?<prefix>^|\/)\.ssh\//u,message:"Refuse to read files inside ~/.ssh/."},{rule:"read-ssh-key",pattern:/(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)$/u,message:"Refuse to read SSH private key files."},{rule:"read-private-key",pattern:/\.(?<ext>pem|key|p12|pfx)$/iu,message:"Refuse to read private-key-shaped files."},{rule:"read-aws-credentials",pattern:/(?<prefix>^|\/)\.aws\/credentials$/u,message:"Refuse to read ~/.aws/credentials."},{rule:"read-netrc",pattern:/(?<prefix>^|\/)\.netrc$/u,message:"Refuse to read ~/.netrc (host credentials)."},{rule:"read-secrets-file",pattern:/(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/iu,message:"Refuse to read a file named secrets.{json,yaml,toml,env}."}],Ee=(e)=>{let t=de(e.file_path,"read",$i);if(t!==null)return f(t.rule,t.message);return u("read-protect")};var Hn=new Set(["NODE_OPTIONS","NODE_PATH","PYTHONPATH","PYTHONHOME","PYTHONSTARTUP","LD_PRELOAD","DYLD_INSERT_LIBRARIES","BUN_OPTIONS"]),Vn=(e,t)=>{if(e.environmentAssignments?.some((n)=>Hn.has(n))===!0)return!0;return e.invocations.some((n)=>{if(["cd","pushd","popd"].includes(n.head)||t.remoteHeads.has(n.head))return!0;return n.head==="env"&&n.tokens.some((r)=>Hn.has(r.split("=")[0]??""))})},D=(e)=>f("embedded-code",`${e} Use a dedicated file tool, a recoverable deletion tool, or a command Tripwire can fully inspect.`),$n=(e)=>{let t=e,n=[];for(let r=0;r<256;r+=1){try{return q.join(Bi(t),...n.toReversed())}catch(i){if(!(i instanceof Error)||!("code"in i)||i.code!=="ENOENT")return null}try{if(Li(t).isSymbolicLink())return null}catch(i){if(!(i instanceof Error)||!("code"in i)||i.code!=="ENOENT")return null}let s=q.dirname(t);if(s===t)return null;n.push(q.basename(t)),t=s}return null},Mi=(e,t)=>{if(e===""||e.includes("\x00"))return!1;if(!ee(e,t.safePaths.relative,t.safePaths.absolute))return!1;let n=$n(q.resolve(t.cwd,e)),r=$n(t.cwd);if(n===null||r===null||n===q.parse(n).root||n===r)return!1;let s=q.isAbsolute(e)?n:q.relative(r,n);return ee(s,t.safePaths.relative,t.safePaths.absolute)},Ln=(e)=>`'${e.replaceAll("'",String.raw`'\''`)}'`,Ui=(e,t)=>{if(e.kind==="process")return t.inspectCommand(e.argv.map(Ln).join(" "));let n=q.resolve(t.cwd,e.path);if((e.kind==="delete"||e.kind==="truncate")&&!Mi(e.path,t))return D(`${e.kind} outside a verified safe scope: ${JSON.stringify(e.path)}.`);return e.kind==="read"?Ee({file_path:n}):ue({file_path:n,content:""})},lt=(e,t,n)=>{let r=Wn(e,t);if(r.gap!==null)return D(r.gap);return V(r.operations.map((s)=>Ui(s,n)))},Bn=(e,t)=>{let n=[];for(let r of e.invocations){let s=Fn(r);if(s.kind==="blocked")return D(s.reason);if(s.kind==="command"){if(Vn(e,t))return D("The forwarded command has an unverified execution context.");n.push(t.inspectCommand(s.argv.map(Ln).join(" ")))}if(s.kind==="source"){if(Vn(e,t))return D("Interpreter startup, working directory, or remote filesystem state is not verified.");n.push(lt(s.language,s.source,t))}}return n.length===0?u("embedded-code"):V(n)};import{readFileSync as ji}from"fs";var Mn=(e,t)=>{let n=new Set(e.split(`
21
16
  `).map((r)=>r.trim()).filter((r)=>r.length>0));return t.split(`
22
- `).filter((r)=>r.trim().length>0).filter((r)=>!n.has(r.trim()))},ot=(e)=>{try{return Fr(e,"utf8")}catch(t){if(t instanceof Error&&"code"in t&&t.code==="ENOENT")return"";throw t}};var $r=[/\bTODO\s*:/i,/\bFIXME\s*:/i,/\bXXX\s*:/i,/\bHACK\s*:/i,/\bfor now\b/i,/\bnot implemented\b/i,/\bNotImplementedError\b/,/\btemp fix\b/i,/\bfallback\b/i,/\bplaceholder\b/i,/\bbackwards?[ -]?compat(?<ibility>ibility)?\b/i,/\bfor later\b/i,/\blater on\b/i,/\bget back to\b/i,/\bI'?ll fix\b/i,/\bto be implemented\b/i,/\bnot yet (?<state>implemented|done)\b/i,/\bstubbed\b/i],Ar=/\.(?<ext>ts|tsx|js|jsx|mjs|cjs|py|rs|go|rb|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sh|zsh|bash|lua|ex|exs|clj|scala|dart)$/i,Wr=/(?<prefix>^|\/)(?<dir>__tests__|tests?|spec|fixtures?|mocks?|__mocks__|stories)(?<suffix>\/|$)|\.(?<ext>test|spec|fixture|mock|stories)\.[^/]+$/i,Br=/tripwire-allow:[ \t]*\S/,Or=(e)=>{if(Br.test(e))return!1;for(let t of $r)if(t.test(e))return!0;return!1},ce=(e)=>{let t=e.file_path;if(!Ar.test(t)||Wr.test(t))return a("lazy-code");let n="content"in e?e.content:e.new_string,r="content"in e?ot(t):e.old_string,o=[];for(let l of rt(r,n))if(Or(l))o.push(l.slice(0,200));if(o.length===0)return a("lazy-code");let s=o.slice(0,3).map((l)=>` \u2022 ${l}`).join(`
23
- `),i=o.length>3?`
24
- \u2026and ${o.length-3} more`:"";return x("lazy-code-marker",["Heads up: line(s) you just added contain words that often signal incomplete or deferred work. The write went through \u2014 this is a flag, not a block.","",'Why this exists: AI coding agents have a strong pull toward stubbing things, deferring "for now," and shipping half-built fallbacks instead of finishing the work in the same turn. The point of this warning is to push back on that pull on every iteration. If you stubbed something out for time, finish it this turn rather than leaving deferred work for later.',"",'If the marker is genuinely permanent \u2014 a real product term ("auth fallback flow", "retry fallback chain", an HTML input placeholder, a public API field literally named "placeholder"), a logging tag, or a comment intentionally left for a human reader \u2014 no action needed. To silence the flag on subsequent edits of that line, append `tripwire-allow: <one-line reason>` (any comment syntax: `//`, `#`, `--`, etc.).',"","Flagged additions:",s+i].join(`
25
- `))};import{spawnSync as Lr}from"child_process";import{Result as Mr,Schema as _}from"effect";var Nr=_.Struct({RuleID:_.String,Description:_.String,StartLine:_.Finite,EndLine:_.Finite,Secret:_.String,Match:_.String}),jr=_.Array(Nr),Ur=["stdin","--report-format","json","--report-path","-","--exit-code","0","--no-banner","--no-color","--log-level","error"],zr=({executable:e,args:t,input:n,timeoutMs:r})=>{let o=Lr(e,t,{encoding:"utf8",input:n,maxBuffer:67108864,stdio:["pipe","pipe","ignore"],timeout:r}),s={status:o.status,stdout:typeof o.stdout==="string"?o.stdout:""};return o.error===void 0?s:{...s,error:o.error}},Gr=(e)=>{if(typeof e!=="object"||e===null||!("code"in e))return;return typeof e.code==="string"?e.code:void 0},Jr=(e)=>{let t=Gr(e);if(t==="ENOENT")return"missing-executable";if(t==="ETIMEDOUT")return"timeout";return"non-zero-exit"},qr=(e)=>{let t=new Map;for(let n of e)t.set(n.RuleID,(t.get(n.RuleID)??0)+1);return[...t.entries()].map(([n,r])=>({rule:n,count:r}))};var Vr=(e,t)=>{let n=e,r=[...t].toSorted((o,s)=>s.Secret.length-o.Secret.length);for(let o of r){if(o.Secret==="")continue;n=n.replaceAll(o.Secret,`[REDACTED:${o.RuleID}]`)}return n},st=(e,t,n=zr)=>{if(e.length===0)return{ok:!0,hits:[],redacted:e};let r;try{r=n({executable:t.executable,args:Ur,input:e,timeoutMs:t.timeoutMs})}catch{return{ok:!1,category:"non-zero-exit"}}if(r.error!==void 0)return{ok:!1,category:Jr(r.error)};if(r.status!==0)return{ok:!1,category:"non-zero-exit"};let o;try{o=JSON.parse(r.stdout)}catch{return{ok:!1,category:"malformed-json"}}let s=_.decodeUnknownResult(jr)(o);if(Mr.isFailure(s))return{ok:!1,category:"malformed-json"};let i=s.success;return{ok:!0,hits:qr(i),redacted:Vr(e,i)}};var Kr=(e)=>`Tripwire could not verify this tool output because the configured secret scanner failed (${e}). The original output was withheld. Install Betterleaks 1.5.0 or later, check secretScanner.executable and secretScanner.timeoutMs, then run the original tool again.`,it=(e)=>{let t=Ne(e.toolName,e.response);if(t.length===0)return a("post-secret-scrub");let n=st(t,e.secretScanner,e.scannerRunner);if(!n.ok)return d("secret-scanner-failed",Kr(n.category));if(n.hits.length===0)return a("post-secret-scrub");let r=n.hits.map((o)=>`${o.rule}\xD7${o.count}`).join(", ");return d("secrets-in-output",[`tripwire intercepted ${n.hits.length} secret pattern(s) in this tool's output (${r}). The original output was withheld so the secret never enters the model context. A redacted form is below \u2014 work from this, do not re-run the same command in a way that re-fetches the underlying secret.`,"","Redacted output:",n.redacted.slice(0,16000)+(n.redacted.length>16000?`
17
+ `).filter((r)=>r.trim().length>0).filter((r)=>!n.has(r.trim()))},Un=(e)=>{try{return ji(e,"utf8")}catch(t){if(t instanceof Error&&"code"in t&&t.code==="ENOENT")return"";throw t}};var zi=[/\bTODO\s*:/iu,/\bFIXME\s*:/iu,/\bXXX\s*:/iu,/\bHACK\s*:/iu,/\bfor now\b/iu,/\bnot implemented\b/iu,/\bNotImplementedError\b/u,/\btemp fix\b/iu,/\bfallback\b/iu,/\bplaceholder\b/iu,/\bbackwards?[ -]?compat(?<ibility>ibility)?\b/iu,/\bfor later\b/iu,/\blater on\b/iu,/\bget back to\b/iu,/\bI'?ll fix\b/iu,/\bto be implemented\b/iu,/\bnot yet (?<state>implemented|done)\b/iu,/\bstubbed\b/iu],qi=/\.(?<ext>ts|tsx|js|jsx|mjs|cjs|py|rs|go|rb|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sh|zsh|bash|lua|ex|exs|clj|scala|dart)$/iu,Gi=/(?<prefix>^|\/)(?<dir>__tests__|tests?|spec|fixtures?|mocks?|__mocks__|stories)(?<suffix>\/|$)|\.(?<ext>test|spec|fixture|mock|stories)\.[^/]+$/iu,Ji=/tripwire-allow:[ \t]*\S/u,Ki=(e)=>{if(Ji.test(e))return!1;for(let t of zi)if(t.test(e))return!0;return!1},ct=(e)=>{let t=e.file_path;if(!qi.test(t)||Gi.test(t))return u("lazy-code");let n="content"in e?e.content:e.new_string,r="content"in e?Un(t):e.old_string,s=[];for(let a of Mn(r,n))if(Ki(a))s.push(a.slice(0,200));if(s.length===0)return u("lazy-code");let i=s.slice(0,3).map((a)=>` \u2022 ${a}`).join(`
18
+ `),o=s.length>3?`
19
+ \u2026and ${s.length-3} more`:"";return T("lazy-code-marker",["Heads up: line(s) you just added contain words that often signal incomplete or deferred work. The write went through \u2014 this is a flag, not a block.","",'Why this exists: AI coding agents have a strong pull toward stubbing things, deferring "for now," and shipping half-built fallbacks instead of finishing the work in the same turn. The point of this warning is to push back on that pull on every iteration. If you stubbed something out for time, finish it this turn rather than leaving deferred work for later.',"",'If the marker is genuinely permanent \u2014 a real product term ("auth fallback flow", "retry fallback chain", an HTML input placeholder, a public API field literally named "placeholder"), a logging tag, or a comment intentionally left for a human reader \u2014 no action needed. To silence the flag on subsequent edits of that line, append `tripwire-allow: <one-line reason>` (any comment syntax: `//`, `#`, `--`, etc.).',"","Flagged additions:",i+o].join(`
20
+ `))};import{spawnSync as Zi}from"child_process";import{Result as Qi,Schema as M}from"effect";var Xi=M.Struct({RuleID:M.String,Description:M.String,StartLine:M.Finite,EndLine:M.Finite,Secret:M.String,Match:M.String}),Yi=M.Array(Xi),eo=["stdin","--report-format","json","--report-path","-","--exit-code","0","--no-banner","--no-color","--log-level","error"],to=({executable:e,args:t,input:n,timeoutMs:r})=>{let s=Zi(e,t,{encoding:"utf8",input:n,maxBuffer:67108864,stdio:["pipe","pipe","ignore"],timeout:r}),i={status:s.status,stdout:typeof s.stdout==="string"?s.stdout:""};return s.error===void 0?i:{...i,error:s.error}},no=(e)=>{if(typeof e!=="object"||e===null||!("code"in e))return;return typeof e.code==="string"?e.code:void 0},ro=(e)=>{let t=no(e);if(t==="ENOENT")return"missing-executable";if(t==="ETIMEDOUT")return"timeout";return"non-zero-exit"},so=(e)=>{let t=new Map;for(let n of e)t.set(n.RuleID,(t.get(n.RuleID)??0)+1);return[...t.entries()].map(([n,r])=>({rule:n,count:r}))};var io=(e,t)=>{let n=e,r=[...t].toSorted((s,i)=>i.Secret.length-s.Secret.length);for(let s of r)if(s.Secret!=="")n=n.replaceAll(s.Secret,`[REDACTED:${s.RuleID}]`);return n},jn=(e,t,n=to)=>{if(e.length===0)return{ok:!0,hits:[],redacted:e};let r;try{r=n({executable:t.executable,args:eo,input:e,timeoutMs:t.timeoutMs})}catch{return{ok:!1,category:"non-zero-exit"}}if(r.error!==void 0)return{ok:!1,category:ro(r.error)};if(r.status!==0)return{ok:!1,category:"non-zero-exit"};let s;try{s=JSON.parse(r.stdout)}catch{return{ok:!1,category:"malformed-json"}}let i=M.decodeUnknownResult(Yi)(s);if(Qi.isFailure(i))return{ok:!1,category:"malformed-json"};let o=i.success;return{ok:!0,hits:so(o),redacted:io(e,o)}};var oo=(e)=>`Tripwire could not verify this tool output because the configured secret scanner failed (${e}). The original output was withheld. Install Betterleaks 1.5.0 or later, check secretScanner.executable and secretScanner.timeoutMs, then run the original tool again.`,zn=(e)=>{let t=Qt(e.toolName,e.response);if(t.length===0)return u("post-secret-scrub");let n=jn(t,e.secretScanner,e.scannerRunner);if(!n.ok)return f("secret-scanner-failed",oo(n.category));if(n.hits.length===0)return u("post-secret-scrub");let r=n.hits.map((s)=>`${s.rule}\xD7${s.count}`).join(", ");return f("secrets-in-output",[`tripwire intercepted ${n.hits.length} secret pattern(s) in this tool's output (${r}). The original output was withheld so the secret never enters the model context. A redacted form is below \u2014 work from this, do not re-run the same command in a way that re-fetches the underlying secret.`,"","Redacted output:",n.redacted.slice(0,16000)+(n.redacted.length>16000?`
26
21
  \u2026[truncated]`:"")].join(`
27
- `))};var Zr=[{rule:"read-env",pattern:/(?<prefix>^|\/)\.env(?<ext>\.[^/]+)?$/,message:".env files hold secrets that should never enter the model context. Refuse to read. If the goal is documenting required env vars, look at .env.example or describe the schema from memory."},{rule:"read-dev-vars",pattern:/(?<prefix>^|\/)\.dev\.vars(?<ext>\.[^/]+)?$/,message:"Refuse to read .dev.vars (Cloudflare/Wrangler secrets)."},{rule:"read-ssh",pattern:/(?<prefix>^|\/)\.ssh\//,message:"Refuse to read files inside ~/.ssh/."},{rule:"read-ssh-key",pattern:/(?<prefix>^|\/)(?<key>id_rsa|id_ed25519|id_ecdsa|id_dsa)$/,message:"Refuse to read SSH private key files."},{rule:"read-private-key",pattern:/\.(?<ext>pem|key|p12|pfx)$/i,message:"Refuse to read private-key-shaped files."},{rule:"read-aws-credentials",pattern:/(?<prefix>^|\/)\.aws\/credentials$/,message:"Refuse to read ~/.aws/credentials."},{rule:"read-netrc",pattern:/(?<prefix>^|\/)\.netrc$/,message:"Refuse to read ~/.netrc (host credentials)."},{rule:"read-secrets-file",pattern:/(?<prefix>^|\/)secrets?\.(?<ext>json|ya?ml|toml|env)$/i,message:"Refuse to read a file named secrets.{json,yaml,toml,env}."}],at=(e)=>{let t=O(e.file_path,"read",Zr);if(t!==null)return d(t.rule,t.message);return a("read-protect")};var Xr=(e,t)=>e.some((n)=>n.startsWith("-")&&!n.startsWith("--")&&n.slice(1).includes(t)),Yr=(e,t)=>{if(!t.executables.includes(e.head))return!1;let n=e.tokens.slice(1),r=new Set(n);if(!(t.match?.argumentsIncludeAll??[]).every((o)=>r.has(o)))return!1;if(!(t.match?.argumentsStartWith??[]).every((o,s)=>n[s]===o))return!1;return(t.match?.shortFlagsIncludeAll??[]).every((o)=>Xr(e.flags,o))},ct=(e,t,n)=>{if(k(t))return a("tool-policy");for(let r of e)for(let o of n)if(Yr(r,o))return o.action==="deny"?d(o.rule,o.message):x(o.rule,o.message);return a("tool-policy")};var Qr=async()=>{let e=[];for await(let t of process.stdin){let n=t;if(n instanceof Uint8Array)e.push(Buffer.from(n));else throw TypeError("Tripwire received a non-byte stdin chunk")}return Buffer.concat(e).toString("utf8")},ue={kind:"native"},lt=250,eo="--tripwire-hook",to=q.Array(re).check(q.isMinLength(1)),W=(e=ue)=>{if(e.kind==="cursor"&&!e.post){process.stdout.write(`{"continue":true,"permission":"allow"}
22
+ `))};var ao=(e,t)=>e.some((n)=>n.startsWith("-")&&!n.startsWith("--")&&n.slice(1).includes(t)),lo=(e,t)=>{if(!t.executables.includes(e.head))return!1;let n=e.tokens.slice(1),r=new Set(n);if(!(t.match?.argumentsIncludeAll??[]).every((s)=>r.has(s)))return!1;if(!(t.match?.argumentsStartWith??[]).every((s,i)=>n[i]===s))return!1;return(t.match?.shortFlagsIncludeAll??[]).every((s)=>ao(e.flags,s))},qn=(e,t)=>{for(let n of e.invocations)for(let r of t)if(lo(n,r))return r.action==="deny"?f(r.rule,r.message):T(r.rule,r.message);return u("tool-policy")};var co=async()=>{let e=[];for await(let t of process.stdin){let n=t;if(n instanceof Uint8Array)e.push(Buffer.from(n));else throw TypeError("Tripwire received a non-byte stdin chunk")}return Buffer.concat(e).toString("utf8")},ft={kind:"native"},Jn=250,uo="--tripwire-hook",fo=Re.Array(Qe).check(Re.isMinLength(1)),oe=(e=ft)=>{if(e.kind==="cursor"&&!e.post){process.stdout.write(`{"continue":true,"permission":"allow"}
28
23
  `);return}process.stdout.write(`{"continue": true}
29
- `)},le=(e)=>`[tripwire:${e.rule}] ${e.message}`,pt=(e)=>{let t=e.kind==="ask"?`${le(e)} This unattended Cursor run cannot ask a human, so the action is denied.`:le(e);process.stdout.write(`${JSON.stringify({continue:!0,permission:"deny",user_message:t,agent_message:t})}
30
- `)},yt=(e)=>e.turn_id!==void 0,dt=(e,t,n)=>{if(n.kind==="cursor"){process.stdout.write(`${JSON.stringify({continue:!0,permission:"allow",agent_message:le(t)})}
31
- `);return}let r=e.hook_event_name,o=`[tripwire:${t.rule}] ${t.message}`;if(yt(e)){process.stdout.write(`${JSON.stringify({continue:!0,systemMessage:o})}
32
- `);return}let s={hookEventName:r,additionalContext:o};process.stdout.write(`${JSON.stringify({continue:!0,hookSpecificOutput:s})}
33
- `)},de=(e,t,n=ue)=>{if(n.kind==="cursor"){pt(t);return}let r={hookSpecificOutput:{hookEventName:e,permissionDecision:t.kind==="deny"?"deny":"ask",permissionDecisionReason:`[tripwire:${t.rule}] ${t.message}`}};process.stdout.write(`${JSON.stringify(r)}
34
- `)},no=(e,t)=>{if(t.kind==="cursor"){W(t);return}let n={continue:!0,decision:"block",reason:`[tripwire:${e.rule}] ${e.message}`};process.stdout.write(`${JSON.stringify(n)}
35
- `)},V=(e)=>{let t=e.toLowerCase();if(t==="bash"||t==="exec"||t==="shell"||t==="run_command")return"Bash";if(t==="read"||t==="read_file")return"Read";if(t==="write"||t==="write_file")return"Write";if(t==="edit"||t==="edit_file"||t==="multiedit"||t==="apply_patch")return"Edit";if(t==="webfetch"||t==="web_fetch"||t==="fetch")return"WebFetch";if(t==="powershell")return"PowerShell";return e};class kt extends mt.TaggedError("RuleExecutionError"){}class bt extends mt.TaggedError("HookInputParseError"){}var ro=(e,t,n)=>S.gen(function*(){let o=yield*S.exit(S.try({try:t,catch:(s)=>new kt({cause:s})}).pipe(S.timeout(n)));if(J.isSuccess(o))return o.value;return v(e,L.pretty(o.cause)),a(e)}),St=(e,t,n)=>{let r=[];if(e==="PowerShell")return r.push({name:"powershell-unsupported",fn:()=>d("powershell-unsupported","PowerShell commands are blocked because Tripwire cannot parse PowerShell grammar. Use the Bash tool, or add a PowerShell parser before you enable this tool.")}),r;if(e==="Bash"&&Be(t)){let i=t,l=I(i.command);return r.push({name:"bash-deny",fn:()=>ze(l,i.command)},{name:"bash-git",fn:()=>Je(l,i.command,n.git)},{name:"bash-scoped-rm",fn:()=>Ze(l,i.command,n.safePaths)},{name:"bash-redirect",fn:()=>Ke(l,i.command)},{name:"bash-network-install",fn:()=>qe(l,i.command)},{name:"bash-tar-explosion",fn:()=>Ye(l,i.command)},{name:"tool-policy",fn:()=>ct(l,i.command,n.toolPolicies)},{name:"config-custom",fn:()=>nt(l,i.command,n.blockedCommands,n.allowedCommands)}),r}if(e==="Read"&&Me(t)){let i=t;return r.push({name:"read-protect",fn:()=>at(i)}),r}let o=(e==="Edit"||e==="MultiEdit")&&Oe(t),s=e==="Write"&&Le(t);if(o){let i=t;r.push({name:"path-protect",fn:()=>ie(i)},{name:"lazy-code",fn:()=>ce(i)})}else if(s){let i=t;r.push({name:"path-protect",fn:()=>ie(i)},{name:"lazy-code",fn:()=>ce(i)})}return r},wt=(e,t,n)=>{if(e==="Bash"||e==="PowerShell"||e==="Read"||e==="WebFetch"){let r=e==="PowerShell"?"Bash":e;return[{name:"post-secret-scrub",fn:()=>it({toolName:r,response:t,secretScanner:n})}]}return[]},ut=(e,t)=>S.gen(function*(){if(e.length===0)return a("no-rules");let r=[];for(let o of e)r.push(yield*ro(o.name,o.fn,t));return D(r)}),ft=(e)=>{if(e.length===0)return a("no-rules");let t=[];for(let n of e)try{t.push(n.fn())}catch(r){v(n.name,r),t.push(a(n.name))}return D(t)},oo=(e,t={})=>{let n=z(t),r=V(e.tool_name??"");if(e.hook_event_name==="PreToolUse")return ft(St(r,e.tool_input,n));if(e.hook_event_name==="PostToolUse")return ft(wt(r,e.tool_response,n.secretScanner));return a("no-rules")},so=(e,t,n)=>{let r=e.hook_event_name,o=V(e.tool_name??"");if(r==="PreToolUse"&&o==="Bash"){if(t.kind==="warn"){dt(e,t,n);return}W(n);return}if(t.kind==="warn"){dt(e,t,n);return}W(n)},io=(e)=>`tripwire config at ${U} failed to load, so ALL custom safety policy is inactive. Failing closed until it is fixed. Fix the JSON, then this clears on the next hook call. Restart any long-running consumer that caches the loaded config. Error: ${e}`,Rt=()=>{let e=process.argv.indexOf("--cursor-event");if(e!==-1)return process.argv[e+1]??"";return process.argv.find((n)=>n.startsWith("--cursor-event="))?.slice(15)},ao=()=>process.argv.includes(eo),vt=()=>{let e=Rt();return e===void 0?ue:te(e)},P=(e,t=!1)=>{let n=vt();if(t){de("PreToolUse",d("tripwire-batch-error",`Tripwire batch input could not be processed (${e}). Failing closed.`),n);return}if(n.kind==="cursor"&&!n.post){pt(d("cursor-hook-error",`Cursor hook input could not be processed (${e}). Failing closed.`));return}W(n)},gt=(e,t)=>{if(t.kind==="cursor")return`cursor:${t.eventName}`;return yt(e)?"codex":"native"},ht=(e)=>e.length===1?e[0]??a("no-rules"):D(e),co=(e)=>S.gen(function*(){let n=Array.isArray(e);if(n&&!ao())return v("decode","Batch hook input is only available through --tripwire-hook"),P("unsupported batch input"),{ok:!1};let r,o;if(n){let u=yield*S.exit(q.decodeEffect(to)(e));if(J.isFailure(u))return v("decode",L.pretty(u.cause)),P("unsupported batch event shape",!0),{ok:!1};r=u.value,o=vt()}else{let u=We(e,Rt()),f=yield*S.exit(q.decodeUnknownEffect(re)(u.event));if(J.isFailure(f))return v("decode",L.pretty(f.cause)),P("unsupported event shape"),{ok:!1};r=[f.value],{host:o}=u}let[s]=r;if(s===void 0)return P("empty batch",!0),{ok:!1};let i=s.hook_event_name,l=gt(s,o);if(r.some((u)=>u.hook_event_name!==i))return v("decode","Batch hook input mixes event phases"),P("mixed event phases",!0),{ok:!1};if(r.some((u)=>gt(u,o)!==l))return v("decode","Batch hook input mixes hosts"),P("mixed hosts",!0),{ok:!1};return{ok:!0,events:r,firstEvent:s,host:o,phase:i}}),lo=S.gen(function*(){let t=yield*G(),n=yield*S.promise(Qr),r=yield*S.exit(S.try({try:()=>JSON.parse(n),catch:(f)=>new bt({cause:f})}));if(J.isFailure(r)){v("parse",L.pretty(r.cause)),P("invalid JSON");return}let o=yield*co(r.value);if(!o.ok)return;let{events:s,firstEvent:i,host:l,phase:u}=o;if(u==="PreToolUse"){if(!t.ok){de(u,d("config-error",io(t.error)),l);return}let f=[];for(let p of s){let w=V(p.tool_name??"");f.push(yield*ut(St(w,p.tool_input,t.config),lt))}let g=ht(f);if(g.kind==="deny"||g.kind==="ask"){de(u,g,l);return}so(i,g,l);return}if(u==="PostToolUse"){let f=t.ok?t.config.secretScanner:B().secretScanner,g=[];for(let w of s){let C=V(w.tool_name??"");g.push(yield*ut(wt(C,w.tool_response,f),lt))}let p=ht(g);if(p.kind==="deny"){no(p,l);return}W(l);return}W(l)}),Ws=lo.pipe(S.catchCause((e)=>(v("dispatch-fatal",L.pretty(e)),P("dispatcher failure"),S.void)));export{a as allow,y as ask,oo as decide,d as deny,B as getDefaultConfig,En as loadConfig,G as loadConfigResult,z as mergeWithDefaults,x as warn};
24
+ `)},dt=(e)=>`[tripwire:${e.rule}] ${e.message}`,er=(e)=>{let t=e.kind==="ask"?`${dt(e)} This unattended Cursor run cannot ask a human, so the action is denied.`:dt(e);process.stdout.write(`${JSON.stringify({continue:!0,permission:"deny",user_message:t,agent_message:t})}
25
+ `)},tr=(e)=>e.turn_id!==void 0,Kn=(e,t,n)=>{if(n.kind==="cursor"){process.stdout.write(`${JSON.stringify({continue:!0,permission:"allow",agent_message:dt(t)})}
26
+ `);return}let r=e.hook_event_name,s=`[tripwire:${t.rule}] ${t.message}`;if(tr(e)){process.stdout.write(`${JSON.stringify({continue:!0,systemMessage:s})}
27
+ `);return}let i={hookEventName:r,additionalContext:s};process.stdout.write(`${JSON.stringify({continue:!0,hookSpecificOutput:i})}
28
+ `)},ut=(e,t,n=ft)=>{if(n.kind==="cursor"){er(t);return}let r={hookSpecificOutput:{hookEventName:e,permissionDecision:t.kind==="deny"?"deny":"ask",permissionDecisionReason:`[tripwire:${t.rule}] ${t.message}`}};process.stdout.write(`${JSON.stringify(r)}
29
+ `)},po=(e,t)=>{if(t.kind==="cursor"){oe(t);return}let n={continue:!0,decision:"block",reason:`[tripwire:${e.rule}] ${e.message}`};process.stdout.write(`${JSON.stringify(n)}
30
+ `)},ho={bash:"Bash",exec:"Bash",shell:"Bash",run_command:"Bash",read:"Read",read_file:"Read",write:"Write",write_file:"Write",edit:"Edit",edit_file:"Edit",multiedit:"Edit",apply_patch:"Edit",webfetch:"WebFetch",web_fetch:"WebFetch",fetch:"WebFetch",powershell:"PowerShell",exec_command:"ExecCommand","functions.exec_command":"ExecCommand","functions.exec":"Bash",python:"Python",javascript:"CodeRepl",typescript:"CodeRepl",js_repl:"StatefulCode",python_repl:"StatefulCode","functions.js_repl":"StatefulCode"},Pe=(e)=>ho[e.toLowerCase()]??e;class nr extends Yn.TaggedError("RuleExecutionError"){}class rr extends Yn.TaggedError("HookInputParseError"){}var mo=(e,t,n)=>P.gen(function*(){let s=yield*P.exit(P.try({try:t,catch:(i)=>new nr({cause:i})}).pipe(P.timeout(n)));if(Ce.isSuccess(s))return s.value;return O(e,he.pretty(s.cause)),e==="embedded-code"?D("The code inspection rule failed or timed out."):u(e)}),Ie=(e,t,n={},r=0)=>{if(r>8)return[{name:"embedded-code",fn:()=>D("Nested process inspection exceeds its depth limit.")}];let s=[],i=ae(e,{...n,executionCarrierAliases:t.shell.executionCarrierAliases}),o=(a,l)=>({name:a,fn:()=>ne(i,l())});return s.push({name:"embedded-code",fn:()=>Bn(i,sr(t,n.cwd,r))},o("bash-deny",()=>en(i,t.ruleActions)),o("bash-git",()=>rn(i,t.git)),o("bash-scoped-rm",()=>ln(i,t.safePaths)),o("bash-redirect",()=>an(i)),o("bash-network-install",()=>sn(i)),o("bash-tar-explosion",()=>dn(i)),o("tool-policy",()=>qn(i,t.toolPolicies)),o("config-custom",()=>hn(i,t.blockedCommands,t.allowedCommands))),s},sr=(e,t,n=0)=>({safePaths:e.safePaths,remoteHeads:new Set(["ssh",...e.shell.executionCarrierAliases.map((r)=>r.command[0]??"")]),cwd:t??process.cwd(),inspectCommand:(r)=>Ae(Ie(r,e,t===void 0?{}:{cwd:t},n+1))}),go=(e,t,n,r)=>{if(e==="StatefulCode")return[{name:"embedded-code",fn:()=>D("Persistent REPL bindings cannot be verified by a stateless hook. Use a fresh interpreter process.")}];if(e==="ExecCommand"){let s=Gt(t);return Gn.isSome(s)?Ie(s.value.cmd,n,r===void 0?{}:{cwd:r}):[{name:"embedded-code",fn:()=>D("Executable tool input is missing its command.")}]}if(e==="Python"||e==="CodeRepl"||e==="Bash"&&!Xe(t)){let s=qt(t);if(Gn.isNone(s))return[{name:"embedded-code",fn:()=>D("Executable tool input is missing its source text.")}];let i=typeof s.value==="string"?s.value:s.value.code;return[{name:"embedded-code",fn:()=>lt(e==="Python"?"python":"typescript",i,sr(n,r))}]}return},ir=(e,t,n,r)=>{let s=go(e,t,n,r);if(s!==void 0)return s;let i=[];if(e==="PowerShell")return i.push({name:"powershell-unsupported",fn:()=>f("powershell-unsupported","PowerShell commands are blocked because Tripwire cannot parse PowerShell grammar. Use the Bash tool, or add a PowerShell parser before you enable this tool.")}),i;if(e==="Bash"&&Xe(t))return Ie(t.command,n,r===void 0?{}:{cwd:r});if(e==="Read"&&Zt(t)){let l=t;return i.push({name:"read-protect",fn:()=>Ee(l)}),i}let o=(e==="Edit"||e==="MultiEdit")&&Jt(t),a=e==="Write"&&Kt(t);if(o){let l=t;i.push({name:"path-protect",fn:()=>ue(l)},{name:"lazy-code",fn:()=>ct(l)})}else if(a){let l=t;i.push({name:"path-protect",fn:()=>ue(l)},{name:"lazy-code",fn:()=>ct(l)})}return i},or=(e,t,n)=>{if(e==="Bash"||e==="PowerShell"||e==="Read"||e==="WebFetch"){let r=e==="PowerShell"?"Bash":e;return[{name:"post-secret-scrub",fn:()=>zn({toolName:r,response:t,secretScanner:n})}]}return[]},Zn=(e,t)=>P.gen(function*(){if(e.length===0)return u("no-rules");let r=[];for(let s of e)r.push(yield*mo(s.name,s.fn,t));return V(r)}),Ae=(e)=>{if(e.length===0)return u("no-rules");let t=[];for(let n of e)try{t.push(n.fn())}catch(r){O(n.name,r),t.push(n.name==="embedded-code"?D("The code inspection rule failed."):u(n.name))}return V(t)},yo=(e,t={})=>{let n=ce(t),r=Pe(e.tool_name??"");if(e.hook_event_name==="PreToolUse")return Ae(ir(r,e.tool_input,n,e.cwd));if(e.hook_event_name==="PostToolUse")return Ae(or(r,e.tool_response,n.secretScanner));return u("no-rules")},bo=(e,t={},n={})=>Ae(Ie(e,ce(t),n)),So=(e,t,n)=>{let r=e.hook_event_name,s=Pe(e.tool_name??"");if(r==="PreToolUse"&&s==="Bash"){if(t.kind==="warn"){Kn(e,t,n);return}oe(n);return}if(t.kind==="warn"){Kn(e,t,n);return}oe(n)},ko=(e)=>`tripwire config at ${Se} failed to load, so ALL custom safety policy is inactive. Failing closed until it is fixed. Fix the JSON, then this clears on the next hook call. Restart any long-running consumer that caches the loaded config. Error: ${e}`,ar=()=>{let e=process.argv.indexOf("--cursor-event");if(e!==-1)return process.argv[e+1]??"";return process.argv.find((n)=>n.startsWith("--cursor-event="))?.slice(15)},vo=()=>process.argv.includes(uo),lr=()=>{let e=ar();return e===void 0?ft:Ke(e)},G=(e,t=!1)=>{let n=lr();if(t){ut("PreToolUse",f("tripwire-batch-error",`Tripwire batch input could not be processed (${e}). Failing closed.`),n);return}if(n.kind==="cursor"&&!n.post){er(f("cursor-hook-error",`Cursor hook input could not be processed (${e}). Failing closed.`));return}oe(n)},Qn=(e,t)=>{if(t.kind==="cursor")return`cursor:${t.eventName}`;return tr(e)?"codex":"native"},Xn=(e)=>e.length===1?e[0]??u("no-rules"):V(e),wo=(e)=>P.gen(function*(){let n=Array.isArray(e);if(n&&!vo())return O("decode","Batch hook input is only available through --tripwire-hook"),G("unsupported batch input"),{ok:!1};let r,s;if(n){let l=yield*P.exit(Re.decodeEffect(fo)(e));if(Ce.isFailure(l))return O("decode",he.pretty(l.cause)),G("unsupported batch event shape",!0),{ok:!1};r=l.value,s=lr()}else{let l=zt(e,ar()),c=yield*P.exit(Re.decodeUnknownEffect(Qe)(l.event));if(Ce.isFailure(c))return O("decode",he.pretty(c.cause)),G("unsupported event shape"),{ok:!1};r=[c.value],{host:s}=l}let[i]=r;if(i===void 0)return G("empty batch",!0),{ok:!1};let o=i.hook_event_name,a=Qn(i,s);if(r.some((l)=>l.hook_event_name!==o))return O("decode","Batch hook input mixes event phases"),G("mixed event phases",!0),{ok:!1};if(r.some((l)=>Qn(l,s)!==a))return O("decode","Batch hook input mixes hosts"),G("mixed hosts",!0),{ok:!1};return{ok:!0,events:r,firstEvent:i,host:s,phase:o}}),xo=P.gen(function*(){let t=yield*ke(),n=yield*P.promise(co),r=yield*P.exit(P.try({try:()=>JSON.parse(n),catch:(c)=>new rr({cause:c})}));if(Ce.isFailure(r)){O("parse",he.pretty(r.cause)),G("invalid JSON");return}let s=yield*wo(r.value);if(!s.ok)return;let{events:i,firstEvent:o,host:a,phase:l}=s;if(l==="PreToolUse"){if(!t.ok){ut(l,f("config-error",ko(t.error)),a);return}let c=[];for(let x of i){let A=Pe(x.tool_name??"");c.push(yield*Zn(ir(A,x.tool_input,t.config,x.cwd),Jn))}let h=Xn(c);if(h.kind==="deny"||h.kind==="ask"){ut(l,h,a);return}So(o,h,a);return}if(l==="PostToolUse"){let c=t.ok?t.config.secretScanner:le().secretScanner,h=[];for(let A of i){let J=Pe(A.tool_name??"");h.push(yield*Zn(or(J,A.tool_response,c),Jn))}let x=Xn(h);if(x.kind==="deny"){po(x,a);return}oe(a);return}oe(a)}),vc=xo.pipe(P.catchCause((e)=>P.sync(()=>{O("dispatch-fatal",he.pretty(e)),G("dispatcher failure")})));export{u as allow,b as ask,yo as decide,bo as decideBash,f as deny,le as getDefaultConfig,Lr as loadConfig,ke as loadConfigResult,ce as mergeWithDefaults,T as warn};