Can you perform a deep security audit?

HEAD
54eae31chore(release): v0.1.8
This post might have stale content, as HEAD is 470 commits ahead.

thinking of downloading this. do a deep security audit.

Avatar of openai/gpt-5.6-sol
GPT 5.6 Sol
commented

#Security audit summary

Verdict: I found no obvious malware, backdoor, credential-stealing code, or critical renderer-to-OS escape in the reviewed source. The Electron boundary is substantially better hardened than most small desktop projects.

However, GooeyPi is not a low-risk desktop application. It launches coding agents, shells, plugins, MCP servers, and browser automation with your user account’s permissions. I found two concrete medium-severity weaknesses, plus several important design and supply-chain risks.

Recommendation: Reasonable to install for development use if you:

  • use an official, signed release;
  • verify its checksum/provenance;
  • disable browser control unless needed;
  • do not use the in-app browser for sensitive authenticated accounts;
  • avoid third-party plugins/MCP servers initially;
  • preferably run it under a separate OS account or VM for untrusted repositories.

Audit target: repository state corresponding to package.json version 0.1.8. Git was unavailable in the audit environment, so I could not record the exact commit SHA.


##Findings

###1. Capability tokens remain usable after their runtime exits

Severity: Medium

Each agent runtime receives bearer tokens for browser, scheduling, and collaboration brokers. Tokens:

  • remain valid for 24 hours;
  • are stored in each bridge’s claims map;
  • are removed only upon expiration pruning or complete bridge shutdown;
  • have no runtime-exit revocation mechanism.

Relevant code:

  • Token TTL and claim structure: electron/main/lib/capability-bridge.ts:8-18
  • Token creation: electron/main/lib/capability-bridge.ts:75-79
  • Claims are cleared only when the whole bridge stops: electron/main/lib/capability-bridge.ts:86-91
  • Expired claims are pruned only when new environments are created: electron/main/lib/capability-bridge.ts:136-139
  • Browser claim is later bound to a session, but never revoked: electron/main/browser/agent-bridge.ts:35-39

A malicious or compromised agent process could copy its token into a detached helper before termination. That helper could continue to:

  • operate browser tabs;
  • run JavaScript in pages;
  • read the active terminal context;
  • manipulate scoped schedules;
  • communicate with other sessions;

until the token expires or GooeyPi exits.

Process-tree termination reduces accidental leftovers, but a deliberately daemonized/reparented process can be difficult to contain reliably.

Recommended fix:

  • Add revokeToken(token) to CapabilityBridge.
  • Associate every issued token with a runtime ID.
  • Revoke all tokens immediately on runtime close, failed startup, environment refresh, session archive, and project revocation.
  • Reduce the TTL to a short fallback period, such as 5–15 minutes, with renewal only while the owning runtime remains live.
  • Make broker dispatch verify runtime liveness in addition to token validity.

###2. Agent runtimes have no numeric concurrency limit

Severity: Medium

The security documentation says runtimes are capped, but the manager intentionally allows unlimited busy or starting runtimes:

“Busy, queued, compacting, and still-starting runtimes remain alive, with no numeric concurrency cap.”

Source: electron/main/agent-rpc/manager.ts:324-330

The manager replaces an idle runtime, but an attacker that keeps each runtime starting or busy can create an unbounded number of subprocesses. There are three independent managers—Prime, OMP, and Pi—so this can multiply across harnesses.

The main renderer is strongly protected, so ordinary remote pages cannot call this IPC directly. Nevertheless, renderer compromise, a logic bug, aggressive automation, or repeated voice/session operations could cause:

  • process and file-descriptor exhaustion;
  • memory exhaustion;
  • large numbers of provider requests;
  • system instability or unexpected API costs.

Recommended fix:

  • Set hard limits per harness and globally, for example:
    • 4 starting runtimes;
    • 8–16 live runtimes per harness;
    • 24 total.
  • Bound the pending runtime-start admission queue.
  • Reject excess starts with a stable user-facing error.
  • Add tests proving starts cannot exceed the limit.
  • Correct docs/security.md, which currently overstates this protection.

###3. Browser automation is enabled by default and can access authenticated/internal pages

Severity: High operational risk; design issue rather than a conventional vulnerability

The default configuration has:

browserEnabled: true

Source: electron/main/store.ts:50-53

This conflicts with the README’s implication that users enable browser control when wanted. Agent browser tools can:

  • navigate to arbitrary HTTP(S) URLs, including localhost and private-network hosts;
  • read page text and interactive elements;
  • click and type;
  • take screenshots;
  • execute arbitrary JavaScript in the page.

The JavaScript execution primitive is explicit in:

  • electron/main/browser/agent-service.ts:558-561
  • electron/main/browser/page-scripts.ts:166-182

The browser profile is isolated from the main renderer, which is good. But it is persistent, and there is no per-origin or per-action approval. If you log into email, GitHub, cloud consoles, internal dashboards, or financial services in that profile, an agent with browser access can act with that session.

The extension wraps page output in “untrusted content” markers, but prompt-injection warnings are advisory controls, not dependable security boundaries.

GooeyPi’s agents already have broad local execution capabilities, so browser restrictions cannot turn the overall system into a sandbox. Still, persistent authenticated browser sessions add a distinct and easily overlooked capability.

Recommended fix:

  • Default browserEnabled to false.
  • Clearly show an always-visible indicator while an agent has browser access.
  • Add per-origin grants, especially for:
    • loopback;
    • RFC1918/private networks;
    • link-local and cloud metadata addresses.
  • Require confirmation before the first agent action on an authenticated tab.
  • Consider disabling browser_evaluate by default or requiring a separate advanced toggle.
  • Provide an ephemeral browser-profile mode and automatic cookie clearing.
  • Explain that enabling browser control grants access to existing cookies and logged-in pages.

User mitigation: Keep browser control off and do not sign into sensitive services in GooeyPi’s browser.


###4. MCP OAuth discovery can perform GET requests to arbitrary network locations

Severity: Low–Medium

MCP OAuth discovery fetches a user-supplied HTTP(S) server URL and then potentially fetches a metadata URL provided by that server:

  • electron/main/providers.ts:47-68

The implementation correctly rejects redirects, credentials in URLs, and oversized metadata. However, it does not reject:

  • localhost;
  • private network ranges;
  • link-local addresses;
  • cloud metadata endpoints;
  • DNS rebinding.

Local MCP servers are a valid use case, so completely blocking private addresses may not be appropriate. But the current behavior forms an SSRF-like network primitive from the privileged main process.

Recommended fix:

  • Separate “local MCP” from remote MCP configuration.
  • Require explicit confirmation for loopback/private/link-local destinations.
  • Resolve and re-check addresses before connecting.
  • Block cloud metadata ranges by default.
  • Ensure the metadata URL is same-origin unless the user approves the authorization-server origin.

###5. Development mode deliberately trusts a loopback web server

Severity: Low; packaged releases unaffected

In development mode, ELECTRON_RENDERER_URL may point to any uncredentialed loopback HTTP(S) origin:

  • electron/main/index.ts:112-123

Once that page loads successfully, it receives the privileged preload API through the normal authorization path. A hijacked development server or malicious environment variable could therefore gain terminal, Git, agent, plugin, and settings privileges.

Packaged builds ignore this environment variable and use the custom prime-work:// protocol, so official releases are not affected.

Mitigation:

  • Do not run npm run dev in an untrusted environment.
  • Avoid exporting ELECTRON_RENDERER_URL globally.
  • Bind the dev server safely and ensure the chosen port is not shared.
  • Optionally require a random development token in the URL.

###6. The diagnostics setting appears to be non-functional

Severity: Informational/privacy correctness

The UI says:

“Allow anonymous crash and reliability diagnostics.”

Source: src/pages/settings/PrivacySettings.tsx:9-12

But the telemetry field is only persisted and displayed; I found no telemetry sender or crash-upload implementation. Local crash logging exists, but no corresponding diagnostics upload code was identified.

This is safer than undisclosed telemetry, but the setting is misleading.

Recommended fix: Either implement and document the exact diagnostics destination and payload, or relabel it as unavailable/remove the toggle.


##Positive security properties

The desktop boundary is unusually thorough.

###Renderer isolation

The main window uses:

  • nodeIntegration: false
  • contextIsolation: true
  • sandbox: true
  • webSecurity: true
  • allowRunningInsecureContent: false

Source: electron/main/index.ts:230-240

The preload exposes a fixed, frozen API rather than Electron or Node primitives: electron/preload/index.ts:1-143.

IPC requires:

  • an explicitly authorized WebContents;
  • the top-level main frame;
  • an exact trusted renderer URL;
  • current, non-destroyed sender state.

Source: electron/main/ipc.ts:79-109

That substantially limits IPC access from remote webview content and compromised subframes.

###Remote browser isolation

Webview attachment forcibly removes preload access and applies sandboxing:

  • electron/main/index.ts:136-147

Popups are denied, navigation is restricted to credential-free HTTP(S), and browser-profile permissions are denied:

  • electron/main/index.ts:149-157
  • electron/main/index.ts:858-860

Downloads have useful limits:

  • user gesture required;
  • 512 MiB per download;
  • three concurrent downloads;
  • 1 GiB/hour;
  • unique filenames.

Source: electron/main/browser-downloads.ts:1-86

###Content rendering

Model-authored Markdown:

  • does not enable raw HTML;
  • replaces images with placeholders;
  • permits only HTTP(S), mailto, and fragment links.

Source: src/components/MarkdownText.tsx:9-26, 46-104

This is a strong defense against transcript-driven renderer XSS and remote tracking pixels.

###Filesystem authorization

Project grants are canonicalized and tied to filesystem identity using device, inode, and birth time:

  • electron/main/projects.ts:19-65
  • electron/main/projects.ts:100-125

Symlinked project roots are rejected. Broad root and home-folder grants are also rejected. Paths are revalidated before use, and per-harness grants are separated.

Git operations use fixed argument arrays and disable dangerous repository features such as:

  • hooks;
  • external diff commands;
  • signing hooks;
  • clean/smudge filters;
  • global/system config injection.

Source: electron/main/git.ts:15-38, 158-260

This is significantly better than shelling out to git with user-supplied command strings.

###Process and transport safety

Subprocesses use shell: false, bounded input/output, timeouts, and TERM/KILL escalation:

  • electron/main/process-utils.ts:389-470

RPC commands have strict schemas and unknown-key rejection:

  • electron/main/agent-rpc/command-schema.ts:1-142

RPC streams have:

  • 16 MiB read-frame limits;
  • 2 MiB write-frame limits;
  • bounded write queues;
  • bounded pending requests;
  • strict response correlation.

Sources:

  • electron/main/jsonl-limits.ts
  • electron/main/agent-rpc/transport.ts
  • electron/main/agent-rpc/runtime.ts

Terminal instances are owner-bound, project-authorized, capped at eight, and output-rate limited:

  • electron/main/terminal.ts:117-150
  • electron/main/terminal.ts:238-259

###Secret handling

Voice keys use Electron safeStorage. Linux explicitly rejects Electron’s unprotected basic_text fallback:

  • electron/main/voice.ts:40-78

Secret files use mode 0600, and keys are not returned through status IPC. Self-hosted transcription rejects redirects and requires HTTPS except on loopback.

###Packaged-app hardening

Electron fuses disable:

  • RunAsNode;
  • NODE_OPTIONS;
  • debug CLI arguments;
  • extra file:// privileges;
  • loading outside the ASAR.

ASAR integrity and cookie encryption are enabled:

  • scripts/release/after-pack.cjs:16-29

The renderer also has a restrictive packaged CSP in electron/main/index.ts:77-78.


##Supply-chain assessment

###Good controls

  • package-lock.json includes registry integrity hashes.
  • Prime Agent packages are vendored rather than downloaded from the original third-party bucket.
  • Vendored production tarballs are mirrored in scripts/release/dependency-pins.json.
  • CI tests verify tarball hashes against those pins.
  • GitHub Actions are pinned to full commit SHAs.
  • Release publishing receives write permission only in the final job.
  • macOS releases require signing, notarization, stapling, and Gatekeeper verification.
  • Release assets are intended to have SHA256SUMS.txt and GitHub provenance attestations.
  • Package verification checks ASAR layout, Electron fuses, native-module allowlists, architecture, signatures, and size budgets.

###Residual risks

  • The vendored Prime Agent codebase is large and powerful. It contains shell, package, plugin, network, and model-provider functionality. Vendoring establishes immutability, not trustworthiness.
  • Running from source invokes install scripts for native packages and Prime Agent. The Prime Agent postinstall is dormant by default unless bootstrap environment variables are set, but npm ci still executes the repository’s native dependency rebuild path.
  • The lockfile contains many transitive dependencies, including native binaries.
  • Linux packages are not described as repository-signed. Checksums and attestations help only if users actually verify them.
  • Windows releases are optional. If a release is unsigned, do not treat it as equivalent to the notarized macOS build.

The checked Prime Agent postinstall only bootstraps tools/Python when PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 or PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL=1; otherwise it exits. I did not see a default surprise download there.

I could not run npm audit, the test suite, or the build because Node/npm were unavailable in this audit environment. Public advisory searches did not identify direct known issues in the exact node-pty 1.1.0 or zeromq 6.5.0 packages, but that is not a substitute for a full lockfile audit.


##Safe installation procedure

  1. Use GitHub Releases, not a third-party mirror.
  2. Verify the release tag and asset provenance.
  3. Verify the checksum:
    sha256sum -c SHA256SUMS.txt
    
  4. On macOS, verify signing and notarization:
    codesign --verify --deep --strict --verbose=4 /Applications/GooeyPi.app
    spctl --assess --type execute --verbose=4 /Applications/GooeyPi.app
    xcrun stapler validate /Applications/GooeyPi.app
    
  5. On Windows, inspect the installer’s Digital Signatures tab. Avoid unsigned installers.
  6. Start with:
    • browser control disabled;
    • computer use disabled;
    • Ask User disabled;
    • no third-party plugins;
    • no MCP servers;
    • no scheduled tasks.
  7. Add only a narrow project folder—never your home directory.
  8. Test first on a non-sensitive repository.
  9. Do not put production credentials, SSH keys, .env secrets, or cloud configuration inside the granted project unless the agent genuinely needs them.
  10. For untrusted repositories, use a VM/container or a separate OS account with limited credentials.

##Bottom line

I would not classify the repository as malicious, and its Electron/IPC hardening is strong. The main reasons for caution are the inherent power of coding agents and the two lifecycle/resource-control gaps above. For routine personal development on trusted projects, an official verified release is defensible. For company source code, production credentials, or untrusted repositories, isolate it and disable browser/plugin capabilities until needed.


END OF POST

Can you perform a deep security audit? — am-will/gooey-pi