registry  /  @goplausible/ac2-plugin-codex  /  0.2.7

@goplausible/ac2-plugin-codex@0.2.7

AC2 plugin for Codex — connects Codex to AC2 wallet peers over Liquid Auth (FIDO2) + WebRTC DataChannels.

AI Security Review

scanned 2h ago · by lpm-firewall-ai

LPM treats this as warn-only first-party agent extension lifecycle risk. No confirmed malicious attack surface was found. The package is a high-capability Codex agent extension that intentionally bridges Codex to a user-controlled AC2 wallet and can relay approvals, signatures, paid HTTP requests, and wallet chat when enabled.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
Explicit plugin/MCP use via Codex or npx; runtime tools such as ac2_pair, ac2_sign, and make_http_request_with_x402_ac2.
Impact
User-approved wallet signing/payments and remote Codex approval relay; risky if misused, but disclosed and gated by plugin use and wallet approval.
Mechanism
First-party Codex MCP wallet bridge with remote approval and x402 payment tools.
Rationale
This is a disclosed Codex wallet/MCP extension with powerful user-invoked capabilities, but inspection did not find unconsented install-time agent-control mutation, key theft, covert exfiltration, or autonomous fund transfer. The appropriate firewall outcome is a warning for agent extension lifecycle/capability risk, not a publish block.
Evidence
package.jsondist/server.jsREADME.md.mcp.json.codex-plugin/plugin.jsonskills/ac2/SKILL.md~/.codex/ac2/tool-proxy.json~/.codex/ac2/codex-thread.json~/.codex/ac2/attachments/
Network endpoints5
liquidauth.goplausible.xyz/download/regent.apkgithub.com/GoPlausible/codex-plugins.gitgithub.com/GoPlausible/ac2-sdkgoplausible.com127.0.0.1

Decision evidence

public snapshot
AI called this Suspicious at 86.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • dist/server.js is a Codex MCP/plugin entrypoint that exposes wallet signing, x402 payment, reply, pairing, status, and forget tools.
  • dist/server.js spawns `codex app-server` and relays command/file approvals to a paired wallet chat.
  • dist/server.js writes runtime state such as tool-proxy.json, codex-thread.json, attachments, and pairing state under AC2 state dir.
  • .mcp.json configures `npx -y @goplausible/ac2-plugin-codex@0.2.7` as a stdio MCP server.
Evidence against
  • package.json has no preinstall/install/postinstall hooks; only prepublishOnly build checks.
  • README.md and .codex-plugin/plugin.json clearly describe the wallet bridge, signing, remote approvals, and x402 payment behavior.
  • Signing/payment flows call `sendSigningRequest` and require wallet approval; source does not expose private keys or directly drain wallets.
  • Network/payment tools are user-invoked and package-aligned, with x402 discovery/payment requirements and payer selection checks.
  • Local proxy binds to 127.0.0.1 with a random token and blocks ac2_pair/ac2_forget in passive mode.
  • No credential harvesting, covert exfiltration, destructive install behavior, or remote payload loading was found.
Behavioral surface
Source
ChildProcessCryptoEnvironmentVarsEvalFilesystemNativeBindingsNetworkWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedProtestwareTelemetryUrlStrings
ManifestNo manifest risk signals triggered.
scanned 1 file(s), 1.87 MB of source, external domains: 127.0.0.1, ac2.io, api.devnet.solana.com, api.mainnet-beta.solana.com, api.testnet.solana.com, developer.mozilla.org, example.x402.goplausible.xyz, facilitator.goplausible.xyz, github.com, json-schema.org, liquidauth.goplausible.xyz, mainnet-api.4160.nodely.dev, raw.githubusercontent.com, sola.na, testnet-api.4160.nodely.dev, viem.sh, www.w3.org

Source & flagged code

7 flagged · loading source
dist/server.jsView file
6const __filename = __ac2FileURLToPath(import.meta.url); L7: const __dirname = __ac2Dirname(__filename); L8: var Mhe=Object.create;var w3=Object.defineProperty;var Lhe=Object.getOwnPropertyDescriptor;var Bhe=Object.getOwnPropertyNames;var Uhe=Object.getPrototypeOf,zhe=Object.prototype.has... L9: `:""},this._extScope=e,this._scope=new cc.Scope({parent:e}),this._nodes=[new mT]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){retu... L10: || (${a} == "string" && ${i} && ${i} == +${i})`).assign(s,(0,Ir._)`+${i}`);return;case"integer":n.elseIf((0,Ir._)`${a} === "boolean" || ${i} === null L11: || (${a} === "string" && ${i} && ${i} == +${i} && !(${i} % 1))`).assign(s,(0,Ir._)`+${i}`);return;case"boolean":n.elseIf((0,Ir._)`${i} === "false" || ${i} === 0 || ${i} === null`).... L12: || ${a} === "boolean" || ${i} === null`).assign(s,(0,Ir._)`[${i}]`)}}}function I6e({gen:t,parentData:e,parentDataProperty:r},n){t.if((0,Ir._)`${e} !== undefined`,()=>t.assign((0,Ir... L13: missingProperty: ${n}, ... L18: ${new this._window.XMLSerializer().serializeToString(h)}`;return typeof Blob>"u"||this._options.jsdom?Buffer.from(b):new Blob([b],{type:w})}return
Critical
Wallet Drain

Source uses private key material to transfer cryptocurrency funds.

dist/server.jsView on unpkg · L6
109Trigger-reachable chain: manifest.bin -> dist/server.js L109: `)} L110: `}function oke(t,e){if(!t.appCallTrace||!t.disassembly)return"";let r=e;return e!==void 0?r=e:r={maxValueWidth:lC,topOfStackFirst:!1},Ate(t.appCallTrace,t.disassembly,r)}function a... L111: `);return{content:[yn(a)],details:o}}}}var LMe=Be.Object({refresh:Be.Optional(Be.Boolean({description:"DEPRECATED / no-op (plugin v0.0.84+): every `ac2_capabilities` call now hits ... ... L115: `);return{content:[yn(d)],details:a}}}}var $L=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L116: ${t.length}`,r=new TextEncoder().encode(e),n=new Uint8Array(r.length+t.length);return n.set(r,0),n.set(t,r.length),Ks(n)}function DL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L117: `)}var y3={type:"object",properties:{},additionalProperties:!1};function Dh(t){return{content:[{type:"text",text:t}]}}async function whe(t){if(!t)return Dh("No active pairing invit... ... L119: `));if(e.active)return Dh(`Already connected (peer=${e.active.peerDid}). Run ac2_status for details.`);if(e.pendingInvitation&&!yhe(e.pendingInvitation))return whe(e.pen…
Critical
Trigger Reachable Dangerous Capability

A package entrypoint or install-time lifecycle script reaches a source file with blocking dangerous behavior.

dist/server.jsView on unpkg · L109
123`)}catch(o){process.stderr.write(`[ac2] could not write tool-proxy endpoint: ${String(o)} L124: `)}}),r}function Ihe(){let t=She();if(!(t&&t.pid!==process.pid))try{SLe(ZL())}catch{}}function khe(t,e){let r=She();if(!r)return Promise.resolve({content:[{type:"text",text:"The AC... L125: `))i.trim()&&process.stderr.write(`[ac2] app-server: ${i}
High
Child Process

Package source references child process execution.

dist/server.jsView on unpkg · L123
115`);return{content:[yn(d)],details:a}}}}var $L=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L116: ${t.length}`,r=new TextEncoder().encode(e),n=new Uint8Array(r.length+t.length);return n.set(r,0),n.set(t,r.length),Ks(n)}function DL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L117: `)}var y3={type:"object",properties:{},additionalProperties:!1};function Dh(t){return{content:[{type:"text",text:t}]}}async function whe(t){if(!t)return Dh("No active pairing invit... ... L123: `)}catch(o){process.stderr.write(`[ac2] could not write tool-proxy endpoint: ${String(o)} L124: `)}}),r}function Ihe(){let t=She();if(!(t&&t.pid!==process.pid))try{SLe(ZL())}catch{}}function khe(t,e){let r=She();if(!r)return Promise.resolve({content:[{type:"text",text:"The AC... L125: `))i.trim()&&process.stderr.write(`[ac2] app-server: ${i}
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

dist/server.jsView on unpkg · L115
109`)} L110: `}function oke(t,e){if(!t.appCallTrace||!t.disassembly)return"";let r=e;return e!==void 0?r=e:r={maxValueWidth:lC,topOfStackFirst:!1},Ate(t.appCallTrace,t.disassembly,r)}function a... L111: `);return{content:[yn(a)],details:o}}}}var LMe=Be.Object({refresh:Be.Optional(Be.Boolean({description:"DEPRECATED / no-op (plugin v0.0.84+): every `ac2_capabilities` call now hits ... ... L115: `);return{content:[yn(d)],details:a}}}}var $L=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L116: ${t.length}`,r=new TextEncoder().encode(e),n=new Uint8Array(r.length+t.length);return n.set(r,0),n.set(t,r.length),Ks(n)}function DL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L117: `)}var y3={type:"object",properties:{},additionalProperties:!1};function Dh(t){return{content:[{type:"text",text:t}]}}async function whe(t){if(!t)return Dh("No active pairing invit... ... L119: `));if(e.active)return Dh(`Already connected (peer=${e.active.peerDid}). Run ac2_status for details.`);if(e.pendingInvitation&&!yhe(e.pendingInvitation))return whe(e.pendingInvitat... L120: `)}async function vLe(t,e){return e.
High
Command Output Exfiltration

Source combines command execution, command-output handling, and outbound requests; review data flow before blocking.

dist/server.jsView on unpkg · L109
11|| (${a} === "string" && ${i} && ${i} == +${i} && !(${i} % 1))`).assign(s,(0,Ir._)`+${i}`);return;case"boolean":n.elseIf((0,Ir._)`${i} === "false" || ${i} === 0 || ${i} === null`).... L12: || ${a} === "boolean" || ${i} === null`).assign(s,(0,Ir._)`[${i}]`)}}}function I6e({gen:t,parentData:e,parentDataProperty:r},n){t.if((0,Ir._)`${e} !== undefined`,()=>t.assign((0,Ir... L13: missingProperty: ${n},
Low
Eval

Package source references a known benign dynamic code generation pattern.

dist/server.jsView on unpkg · L11
6const __filename = __ac2FileURLToPath(import.meta.url); L7: const __dirname = __ac2Dirname(__filename); L8: var Mhe=Object.create;var w3=Object.defineProperty;var Lhe=Object.getOwnPropertyDescriptor;var Bhe=Object.getOwnPropertyNames;var Uhe=Object.getPrototypeOf,zhe=Object.prototype.has... L9: `:""},this._extScope=e,this._scope=new cc.Scope({parent:e}),this._nodes=[new mT]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){retu... L10: || (${a} == "string" && ${i} && ${i} == +${i})`).assign(s,(0,Ir._)`+${i}`);return;case"integer":n.elseIf((0,Ir._)`${a} === "boolean" || ${i} === null L11: || (${a} === "string" && ${i} && ${i} == +${i} && !(${i} % 1))`).assign(s,(0,Ir._)`+${i}`);return;case"boolean":n.elseIf((0,Ir._)`${i} === "false" || ${i} === 0 || ${i} === null`).... L12: || ${a} === "boolean" || ${i} === null`).assign(s,(0,Ir._)`[${i}]`)}}}function I6e({gen:t,parentData:e,parentDataProperty:r},n){t.if((0,Ir._)`${e} !== undefined`,()=>t.assign((0,Ir... L13: missingProperty: ${n}, ... L18: ${new this._window.XMLSerializer().serializeToString(h)}`;return typeof Blob>"u"||this._options.jsdom?Buffer.from(b):new Blob([b],{type:w})}return
Low
Weak Crypto

Package source references weak cryptographic algorithms.

dist/server.jsView on unpkg · L6

Findings

2 Critical4 High4 Medium8 Low
CriticalWallet Draindist/server.js
CriticalTrigger Reachable Dangerous Capabilitydist/server.js
HighChild Processdist/server.js
HighSame File Env Network Executiondist/server.js
HighCommand Output Exfiltrationdist/server.js
HighObfuscated
MediumNetwork
MediumEnvironment Vars
MediumProtestware
MediumStructural Risk Force Deep Review
LowNon Install Lifecycle Scripts
LowScripts Present
LowEvaldist/server.js
LowWeak Cryptodist/server.js
LowFilesystem
LowHigh Entropy Strings
LowTelemetry
LowUrl Strings