Skip to content

Security

Felipe Marzochi edited this page Jul 27, 2026 · 2 revisions

Security

Source: docs/security/THREAT-MODEL.md and docs/security/SECURITY-ASSESSMENT.md in the repository.

System overview

EGC is a local-first AI memory and orchestration runtime. It has no network services, no authentication surface, and no multi-user access model. The attack surface is limited to the npm package and its dependencies, the two MCP servers running as local stdio processes, the GitHub Actions CI/CD pipeline, and session hooks that process transcript data.

Trust boundaries

Boundary Description
Local filesystem EGC reads and writes state files at ~/.egc/. Access is local-user-only.
AI tool sockets MCP servers communicate with AI tools via stdio. No network exposure.
External dependencies npm packages, pinned via package-lock.json, audited via Dependabot.
GitHub Actions CI/CD runs in ephemeral sandboxes with minimal permissions.

Actors

Actor Trust level
Local user Fully trusted
Contributor Partially trusted: submits PRs, cannot merge without review
Dependency author Untrusted (third-party npm packages)
AI tool (Claude Code, etc.) Trusted at runtime: calls MCP tools, runs in the same user context
GitHub Actions runner Trusted, ephemeral, sandboxed
PR author from a fork Untrusted: fork code does not access repository secrets

Key mitigations

  • Supply chain: dependencies locked via package-lock.json, Dependabot monitors for vulnerabilities, dependency-review.yml blocks PRs introducing high-severity dependencies, npm audit runs on every push.
  • Command injection: egc-guardian's validate_command validates every tool call before execution. Shell commands are built from whitelisted patterns rather than raw string interpolation; execSync was replaced with spawnSync plus argv tokenization to remove a shell-injection surface (v1.1.8).
  • Credential leakage: session hooks sanitize transcript content before writing to disk; environment variable names such as GEMINI_TRANSCRIPT_PATH and EGC_SESSION_ID are replaced with placeholders in log output; state files contain only structured metadata, never raw transcripts.
  • CI/CD: fork pull requests do not get repository secrets; pull_request_target is not used in any workflow; workflows default to permissions: contents: read; the release workflow only triggers on maintainer-pushed version tags; third-party actions are pinned to commit SHAs.
  • State encryption: state files under ~/.egc/state/ are encrypted at rest with AES-256-GCM. A TOCTOU race in the encryption key generation path (loadOrCreateEncKey), where two concurrent egc-memory processes starting before the key file existed could each generate a different key, was fixed in v1.1.9 by making key publication atomic (write-to-temp plus fs.linkSync).
  • Credential path denylist: egc-guardian blocks writes to the specific credential files each AI tool stores (OAuth tokens, session files, API keys) rather than whole config directories, a change made in v1.1.8 after the previous whole-directory block was found to break legitimate functionality without adding real security.
  • Destructive-CLI hard blocks: docker system prune, docker rm/rmi, docker run --privileged or host mounts, gh repo delete, gh api -X DELETE, and prisma migrate reset / --force-reset / db execute return a hard DANGEROUS verdict instead of an advisory warning (v1.1.16, #1041). An absolute-path bypass (/bin/rm, /usr/bin/mv) was closed the same release (#1012).
  • Bash hook dispatcher fails closed: an error in the dispatcher's own plumbing used to fail open, silently disabling every guard (Guardian validate, GateGuard) for that command. It now fails closed instead (v1.1.16, #1019), the most critical finding of that release's security audit.
  • auto_learn write-target validation: target_file is checked against protected paths and must stay inside the project root, closing a write-outside-sandbox path (v1.1.16, #1009).
  • core.hooksPath bypass closed: git hook-path overrides are now matched case-insensitively, closing a DCO/CI bypass (v1.1.16, #1013).
  • republish.yml command injection closed: the version input is validated instead of interpolated directly into a shell command (v1.1.16, #1027).
  • Docker hardened: images run as a non-root user via a multi-stage build that keeps the build toolchain out of the final image, and a .dockerignore keeps .git, .env, and node_modules out of the build context (v1.1.16, #1036, #1028).

Known limitations

EGC has no server component, no authentication, and no network services. Its security posture depends on the security of the host machine and the AI tool integrations it runs inside. Prompt injection from external content read by the AI is an AI-tool-level concern, not addressable at the EGC layer.

Reviewing critical code paths

Path Risk Protection
mcp/servers/egc-guardian/src/index.ts (validate_command) High: gates shell execution Reviewed on every change
install.sh / install.ps1 Medium: modifies global AI tool configs Verified in CI across Linux, macOS, Windows
scripts/hooks/session-end.js Medium: reads transcript, writes to disk Bounded stdin (1 MB cap), structured error handling
mcp/servers/egc-memory/ Low: reads/writes state files only No shell execution, pure file I/O

Reporting a vulnerability

Report privately through GitHub: github.com/Fmarzochi/EGC/security (Security Advisories). Do not open a public issue for a security finding.

Contribution requirement: concurrent-access tests

Any change that reads or writes a file shared across concurrent EGC processes (the encryption key, state files under ~/.egc/state/, install-state files, or any lockfile-like resource under ~/.egc/) must include a concurrent-access regression test before it can be merged. This requirement exists because CodeQL, SonarCloud, and the full OS/Node/package-manager test matrix cannot reason about interleaving between two independent process executions, the bug class that caused the v1.1.9 encryption-key TOCTOU race. See tests/egc-memory-encryption.test.js for the reference pattern.

Clone this wiki locally