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

@goplausible/ac2-plugin-codex@0.2.3

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

AI Security Review

scanned 4h ago · by lpm-firewall-ai

Review flagged AI-agent configuration or capability changes. This remains warn-only unless evidence shows foreign-agent hijack through preinstall/install/postinstall, hidden persistence, exfiltration, remote code execution, or other concrete malicious behavior.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
User installs/enables the Codex plugin or runs the package MCP server, then pairs a wallet.
Impact
A paired wallet user can drive a Codex conversation and approve command/file operations; wallet funds can be used only through exposed payment tools and wallet approval flow.
Mechanism
agent extension with remote approval relay, wallet signing, x402 payment, and Codex app-server bridge
Policy narrative
If enabled and paired, the plugin can bridge a wallet chat into a Codex app-server session, relay command/file approvals to the wallet, and request cryptographic signatures or x402 payments. This is a powerful agent-control surface, but it is package-aligned, documented, user-invoked, and not delivered through an npm install hook or hidden mutation of foreign agent config.
Rationale
Static inspection supports a dangerous but documented Codex plugin capability rather than concrete malicious behavior. Because it exposes remote agent-control and wallet-payment primitives, warn is appropriate, but publish blocking is not supported by the source facts.
Evidence
package.jsondist/server.js.mcp.json.codex-plugin/plugin.jsonskills/ac2/SKILL.mdREADME.md~/.codex/ac2/tool-proxy.json~/.codex/ac2/codex-thread.json
Network endpoints5
liquidauth.goplausible.xyz/download/regent.apkgithub.com/GoPlausible/ac2-sdkgithub.com/GoPlausible/codex-plugins.gitgoplausible.com127.0.0.1

Decision evidence

public snapshot
AI called this Suspicious at 82.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for warning
  • dist/server.js registers MCP tools for wallet signing, transaction signing, x402 paid HTTP requests, and a wallet chat reply bridge.
  • dist/server.js spawns `codex app-server` and starts a Codex thread with `approvalPolicy:"on-request"` and `sandbox:"workspace-write"`.
  • dist/server.js relays command/file-change approvals to the connected wallet and accepts `yes/no <code>` responses from that remote channel.
  • skills/ac2/SKILL.md instructs agents to use `ac2_sign` for signatures and `make_http_request_with_x402_ac2` for wallet-approved payments.
  • README.md recommends `default_tools_approval_mode = "approve"` for the AC2 MCP server, expanding agent-facing capability.
  • dist/server.js writes runtime state/proxy files under the AC2 state directory, including `tool-proxy.json` and `codex-thread.json`.
Evidence against
  • package.json has no install/postinstall/preinstall lifecycle hook; `prepublishOnly` is publisher-side only.
  • The `.mcp.json` and `.codex-plugin/plugin.json` are packaged manifests, not lifecycle code that mutates foreign agent config on install.
  • README.md and skill text repeatedly state wallet signatures/payments require user approval in the wallet.
  • dist/server.js blocks `ac2_pair` and `ac2_forget` through the passive tool proxy and warns wallet-originated text is prompt-injection-prone.
  • No source evidence of private key harvesting; signing is requested from the paired wallet over AC2.
  • Network and payment behavior is documented and exposed through user-invoked MCP/plugin tools.
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 $he=Object.create;var y3=Object.defineProperty;var Che=Object.getOwnPropertyDescriptor;var Dhe=Object.getOwnPropertyNames;var Phe=Object.getPrototypeOf,Mhe=Object.prototype.has... L9: `:""},this._extScope=e,this._scope=new cc.Scope({parent:e}),this._nodes=[new dT]}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 _6e({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
106Trigger-reachable chain: manifest.bin -> dist/server.js L106: `)} L107: `}function tke(t,e){if(!t.appCallTrace||!t.disassembly)return"";let r=e;return e!==void 0?r=e:r={maxValueWidth:cC,topOfStackFirst:!1},bte(t.appCallTrace,t.disassembly,r)}function r... L108: `);return{content:[yn(a)],details:o}}}}var CMe=Be.Object({refresh:Be.Optional(Be.Boolean({description:"DEPRECATED / no-op (plugin v0.0.84+): every `ac2_capabilities` call now hits ... ... L112: `);return{content:[yn(d)],details:a}}}}var RL=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L113: ${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 NL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L114: `)}var h3={type:"object",properties:{},additionalProperties:!1};function Ch(t){return{content:[{type:"text",text:t}]}}async function hhe(t){if(!t)return Ch("No active pairing invit... ... L116: `));if(e.active)return Ch(`Already connected (peer=${e.active.peerDid}). Run ac2_status for details.`);if(e.pendingInvitation&&!mhe(e.pendingInvitation))return hhe(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 · L106
120`)}catch(o){process.stderr.write(`[ac2] could not write tool-proxy endpoint: ${String(o)} L121: `)}}),r}function _he(){let t=vhe();if(!(t&&t.pid!==process.pid))try{vLe(qL())}catch{}}function She(t,e){let r=vhe();if(!r)return Promise.resolve({content:[{type:"text",text:"The AC... L122: `))i.trim()&&process.stderr.write(`[ac2] app-server: ${i}
High
Child Process

Package source references child process execution.

dist/server.jsView on unpkg · L120
112`);return{content:[yn(d)],details:a}}}}var RL=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L113: ${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 NL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L114: `)}var h3={type:"object",properties:{},additionalProperties:!1};function Ch(t){return{content:[{type:"text",text:t}]}}async function hhe(t){if(!t)return Ch("No active pairing invit... ... L120: `)}catch(o){process.stderr.write(`[ac2] could not write tool-proxy endpoint: ${String(o)} L121: `)}}),r}function _he(){let t=vhe();if(!(t&&t.pid!==process.pid))try{vLe(qL())}catch{}}function She(t,e){let r=vhe();if(!r)return Promise.resolve({content:[{type:"text",text:"The AC... L122: `))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 · L112
106`)} L107: `}function tke(t,e){if(!t.appCallTrace||!t.disassembly)return"";let r=e;return e!==void 0?r=e:r={maxValueWidth:cC,topOfStackFirst:!1},bte(t.appCallTrace,t.disassembly,r)}function r... L108: `);return{content:[yn(a)],details:o}}}}var CMe=Be.Object({refresh:Be.Optional(Be.Boolean({description:"DEPRECATED / no-op (plugin v0.0.84+): every `ac2_capabilities` call now hits ... ... L112: `);return{content:[yn(d)],details:a}}}}var RL=Be.Object({message_base64:Be.String({minLength:1,description:"Base64 of the raw bytes that were signed (same bytes the signer received... L113: ${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 NL(t,e){let r=e.slice(0,32),n=e.slice(32,64),i=e[6... L114: `)}var h3={type:"object",properties:{},additionalProperties:!1};function Ch(t){return{content:[{type:"text",text:t}]}}async function hhe(t){if(!t)return Ch("No active pairing invit... ... L116: `));if(e.active)return Ch(`Already connected (peer=${e.active.peerDid}). Run ac2_status for details.`);if(e.pendingInvitation&&!mhe(e.pendingInvitation))return hhe(e.pendingInvitat... L117: `)}async function fLe(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 · L106
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 _6e({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 $he=Object.create;var y3=Object.defineProperty;var Che=Object.getOwnPropertyDescriptor;var Dhe=Object.getOwnPropertyNames;var Phe=Object.getPrototypeOf,Mhe=Object.prototype.has... L9: `:""},this._extScope=e,this._scope=new cc.Scope({parent:e}),this._nodes=[new dT]}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 _6e({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