Warn by default; block when configured. The package gains automatic execution in the consumer's Claude Code tool workflow without an explicit setup command.
Installable DevFlow distribution containing the CLI, runtime hooks, and Claude plugin.
AI Security Review
scanned 26d ago · by lpm-firewall-ai
LPM flags this version as an AI-agent control-surface risk. Installation silently modifies the consumer project's Claude Code hook configuration. The added wildcard hook executes this package's handler before tool use.
The package gains automatic execution in the consumer's Claude Code tool workflow without an explicit setup command.
Mechanism
Postinstall registration of a wildcard Claude PreToolUse command hook
Policy narrative
During npm installation, the lifecycle script locates the consumer project, creates .claude if needed, and merges a PreToolUse hook into hooks.json. The hook matches every tool call and runs the package's pre-tool-use handler. This is an unconsented install-time mutation of a foreign AI-agent control surface, creating persistent automatic execution after installation.
AI rationale
The automatic postinstall hook writes a wildcard command into the consumer's Claude Code configuration. This meets the policy for malicious install-hook abuse even without proven secret theft.
Avoid installing @devflow-tools/devflow@0.18.18. Remove it from direct dependencies and check your lockfile for transitive copies.
Choose an independently verified alternative or release. This report does not establish that other versions are safe.
If this version ran, investigate the affected machine and build environment. Rotate credentials it could access and rebuild from a trusted environment.
1import {createServer,createConnection}from'net';import {createHash,randomUUID as randomUUID$1}from'node:crypto';import {execFileSync,spawn}from'node:child_process';import {dirname,join as join$1}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync
2import {createRequire as createRequire$1,isBuiltin}from'node:module';import {join as join$1,resolve,basename,dirname,relative,sep,isAbsolute,extname}from'node:path';import {createRuntimeManager,resolveStableProjectIdentity,loadPlugins,getPluginInstallRoot,resolveProjectCapabilityProfile,stableRuntimeHash,readPersistedClaudeHookNode,resolveClaudeHookRuntime,verifyClaudeHookNativeRuntime,redactMcpInventory,readMcpInventory,checkMcpHealth,readCachedMcpHealth,DOCTOR_SCHEMA_VERSION,getLocalHmacSecret,createDefaultHostAdapterRegistry,acquireRuntimeHttpCircuit,registerRuntimeHttpSuccess,registerRunti ...
3`)}var Co=D(()=>{});function xt(){let e=dirname(fileURLToPath(import.meta.url)),t=[join$1(e,"..","plugin-files"),join$1(e,"plugin"),join$1(e,"..","..","dist","plugin-files"),join$1(e,"..","..","..","..","plugins","claude-code")];for(let n of t)if(existsSync$1(join$1(n,".claude-plugin","plugin.json")))return n;throw new Error("Plugin files not found. Run `
4`)}function ns(t,e){return {candidateId:t.id,version:t.overlayVersion,state:e,instruction:t.instruction,trigger:t.trigger,confidence:t.confidence,evidenceReceipt:t.graderReceipt??`candidate:${t.id}:v${t.overlayVersion}`,expiresAt:t.expiresAt}}function os(t,e){let n=Math.max(0,Math.min(e.maxLearnedEntries??3,3)),o=Math.max(0,Math.min(e.learnedCharacterBudget??1600,4e3)),r=[],i=0;for(let s of t){if(r.length>=n)break;let a=s.instruction.length+4;i+a>o||(r.push(s),i+=a);}return r}function rs(t,e,n){let o=t.trigger,r=e.replace(/^devflow:/,"").split(":").pop()??e,i=Ce(o.skills);if(i.length>0&&!i.som ...
5`,{mode:384});}function gs(t){let e=createRequire(import.meta.url),o=[process.env.DEVFLOW_SERVER_ENTRY,ys(()=>e.resolve("@devflow-tools/server/dist/main.js")),join(t,"node_modules","@devflow-tools","server","dist","main.js"),join(t,"apps","server","dist","main.js")].filter(r=>!!r).find(existsSync);if(!o)throw new Error("DevFlow server entry point is not installed or built");return {command:process.execPath,args:[o],cwd:t}}function ys(t){try{return t()}catch{return}}var Oe=class{constructor(e){this.projectRoot=e;this.runtime=createRuntimeManager();}addRef(e){let n=mt();n.sessions.includes(e)||n ...
6
7`)}function xo(t){let e=t.runtimeStore??new H(t.projectRoot),n=e.listEvidenceObligations(t.sessionId);if(n.length===0)return ha(t,e),null;let o=xt(t.transcriptPath,n);if(!o.answer){let s=o.degradationReason??"transcript_answer_unavailable";for(let a of n)e.degradeEvidenceObligation(t.sessionId,a.obligationId,s);return null}let r=o.fingerprint;if(!r){for(let s of n)e.degradeEvidenceObligation(t.sessionId,s.obligationId,"transcript_identity_unavailable");return null}let i=[];for(let s of n){let a=Eo(o.answer,[s]);if(a.length===0){e.resolveEvidenceObligation(t.sessionId,s.obligationId),Ne(t,s,"sa ...
8
9---
10
11`)].join(`
12
13`)}}function Eo(t,e){let n=new Set,o=new Set(e.flatMap(c=>c.contract.requiredSections)),r=Ao(t.split(/\r?\n/)),i=To(r.headingLines);for(let c of o)i.found.has(c)||n.add(`missing_section:${c}`);if(e.every(c=>c.contract.runtimeProfilingOccurred))return [...n];let a=e.every(c=>c.contract.samplingEvidenceOccurred);for(let c=0;c<r.claimLines.length;c++){let d=r.claimLines[c].trim();if(!d||Ia(d))continue;let u=i.byLine.get(c);for(let l of Sa(d))ka(l,u,a,n);}return [...n]}function Ao(t){let e=false,n=[],o=[];for(let r of t){if(/^\s*```/.test(r)){e=!e,n.push(""),o.push(r.replace(/^\s*```+[^\s`]*/,""). ...
21DevFlow memory snapshot=degraded; reason=${c}. Memory result completeness is not authoritative; do not conclude that the project has no memories.`:r.memories.length===0&&d.length>0?`## Project memory
5`);}function No(e,t){let n=join$1(e,".claude"),o=join$1(n,"mcp.json"),r={};if(existsSync$1(o))try{r=JSON.parse(readFileSync$1(o,"utf-8"));}catch{console.warn("Warning: Could not parse existing mcp.json, skipping MCP config");return}else mkdirSync$1(n,{recursive:true});let s=r.mcpServers??{};s.devflow||(s.devflow={command:t,args:[],env:{}},r.mcpServers=s,writeFileSync$1(o,JSON.stringify(r,null,2)));}function Ot(e,t,n){let o=join$1(t,qe,sn,n);rmSync(o,{recursive:true,force:true}),mkdirSync$1(o,{recursive:true});let r=join$1(e,"hooks");existsSync$1(r)&&Pt(r,join$1(o,"hooks"));for(let c of ["CLAUD ...
6`);let i=join$1(n,"known_marketplaces.json"),a={};if(existsSync$1(i)){let c;try{c=JSON.parse(readFileSync$1(i,"utf-8"));}catch{throw new Error(`Could not parse Claude marketplace registry: ${i}`)}if(!me(c))throw new Error(`Invalid Claude marketplace registry: ${i}`);a=c;}a[qe]={source:{source:"directory",path:o},installLocation:o,lastUpdated:new Date().toISOString()},writeFileSync$1(i,`${JSON.stringify(a,null,2)}
7`);}function Hi(e,t,n){let o=join$1(e,"..","installed_plugins.json"),r={version:2,plugins:{}};if(existsSync$1(o)){let f;try{f=JSON.parse(readFileSync$1(o,"utf-8"));}catch{throw new Error(`Could not parse Claude plugin registry: ${o}`)}if(!me(f))throw new Error(`Invalid Claude plugin registry: ${o}`);r=f;}else mkdirSync$1(dirname(o),{recursive:true});let s=r.plugins;if(s!==void 0&&!me(s))throw new Error(`Invalid Claude plugin registry plugins: ${o}`);let i=s??{},a=i[Et];if(a!==void 0&&!Array.isArray(a))throw new Error(`Invalid Claude plugin registration for ${Et}`);let c=a??[],l=c.find(f=>me(f) ...
9`)}var Mo,Gi,Uo=D(()=>{Je();Mo=To.default??To,Gi=createRequire(import.meta.url);});var qo={};j(qo,{reviewPluginMemoryHints:()=>na});async function na(e,t,n={}){let o=n.isTTY??!!process.stdout.isTTY,r=new Set(t.split(",").map(oa).filter(Boolean));if(!o||r.size===0)return {available:0,selected:0};let s=await(n.load??loadPlugins)(),i=[];for(let u of r){let p=s.get(u);for(let g of p?.memory?.project?.conventions??[])i.push({id:`${u}:${g.key}`,pluginName:u,convention:g});}if(i.length===0)return {available:0,selected:0};let a=i.map(u=>({name:`${u.pluginName}: ${u.convention.value}`,value:u.id,checke ...
16`);let a=[...r];for(let c of a)try{await Ae(c,e,!0),console.log(`\u2713 ${c}`);}catch{console.error(`\u2717 Failed to install ${c}`);}return a}let s=t.plugins.map(a=>({name:`${a.name.replace("@devflow-tools/","")} - ${a.description.substring(0,50)}`,value:a.name,checked:r.has(a.name)})),i;try{i=await checkbox({message:"Select plugins to install (space to select, enter to confirm):",choices:s});}catch{return console.log(`
17Plugin installation cancelled.
18`),[]}if(i.length>0){console.log(`
19Installing selected plugins...
20`);for(let a of i)try{await Ae(a,e,!0),console.log(`\u2713 ${a}
21`);}catch{console.error(`\u2717 Failed to install ${a}
Long lines were clipped for display.
Lines 221-245javascript
221Stored: ${getGlobalDevFlowDbPath()} (${r.runId})`}async function kd(e={}){let t=e.database??openGlobalDevFlowDatabase(),n,o=null;try{n=e.runId?t.getBenchmarkReport(e.runId):t.getLatestBenchmarkReport(),e.baselineRunId&&(o=t.getBenchmarkReport(e.baselineRunId));}finally{e.database||t.close();}if(!n)throw new Error(e.runId?`Benchmark report "${e.runId}" not found`:"No benchmark reports found. Run `devflow benchmark run` first.");if(e.baselineRunId&&!o)throw new Error(`Benchmark baseline "${e.baselineRunId}" not found`);let r=e.output??"text",s=renderBenchmarkReport(n,r);if(!o||r==="json")return ...
238${S.green("\u2713")} ${n}@${o.version} installed and activated`),console.log(`
239Run "devflow plugin list --installed" to verify.`);}catch(o){console.error(`
240${S.red("\u2717")} Installation failed: ${o.message}`),process.exitCode=1;}});});var si={};j(si,{pluginUninstallCommand:()=>rg});var rg,ii=D(()=>{_e();rg=new Command("uninstall").description("Uninstall a plugin").argument("<name>","Plugin name (with or without @devflow-tools/ prefix)").action(async e=>{let n=`@devflow-tools/plugin-${e.replace(/^@devflow-tools\/(?:plugin-)?/,"")}`;console.log(`Uninstalling ${n}...`);try{await gn(n,process.cwd(),!0),console.log(`
241${S.green("\u2713")} ${n} deactivated and uninstalled`);}catch(o){console.error(`
5${e.stack}`:e+"")}})})});let _=f;functionm(){let e=i.default.useContext(s.RouterContext);if(!e)throw Object.defineProperty(Error("NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted"),"__NEXT_ERROR_CODE",{value:"E509",enumerable:!1,configurable:!0});return e}functiong(...e){return f.router=new u.default(...e),f.readyCallbacks.forEach(e=>e()),f.readyCallbacks=[],f.router}functionE(e){let t={};for(let r of d){if("object"==typeof e[r]){t[r]=Object.assign(Array.isArray(e[r])?[]:{},e[r]);continue}t[r]=e[r]}return t.events=u.default.events,p.forEach(r=>{t[r]=(...t ...