Repository navigation
Security
Source: docs/security/THREAT-MODEL.md and docs/security/SECURITY-ASSESSMENT.md in the repository.
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.
| 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. |
| 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 |
| Other sessions on the bus | Untrusted data: event payloads are never executed as instructions |
-
Supply chain: dependencies locked via
package-lock.jsonin every package (root, both MCP servers, the dashboard), Dependabot monitors for vulnerabilities,dependency-review.ymlblocks PRs introducing high-severity dependencies,npm auditruns on every push, Socket Security and OpenSSF Scorecard run on the repository. Every open advisory in every lockfile was cleared in v1.1.21 (#1338). - Hardening round (v1.1.21): an eighteen-step pass across the memory server, the installer, the hooks and the dashboard, landed in seventeen pull requests (#1356 to #1363, #1365 to #1373), with tighter defaults and stricter checks on what EGC accepts from disk, from the network and from the tools it talks to. Every step carries its own tests and went through review on its pull request; no documented command changed.
-
Command injection:
egc-guardian'svalidate_commandvalidates every tool call before execution. Shell commands are built from whitelisted patterns rather than raw string interpolation;execSyncwas replaced withspawnSyncplus argv tokenization to remove a shell-injection surface (v1.1.8). -
Untrusted content:
validate_contentgives the AI an advisory heuristic scan for prompt-injection patterns in fetched pages, third-party files, API responses and fork diffs before it trusts them. It flags, never blocks. -
Credential leakage: session hooks sanitize transcript content before writing to disk; environment variable names such as
GEMINI_TRANSCRIPT_PATHandEGC_SESSION_IDare replaced with placeholders in log output; state files contain only structured metadata, never raw transcripts. Populated memory never reaches a commit: the propagation files ship as empty structure, a pre-commit hook blocks accidental staging, a git clean filter zeroes the blob even when hooks are bypassed, and a CI guard catches anything that slips past. -
CI/CD: fork pull requests do not get repository secrets;
pull_request_targetis not used in any workflow; workflows default topermissions: contents: read; the release workflow only triggers on maintainer-pushed version tags; third-party actions are pinned to commit SHAs; CodeQL and ClusterFuzzLite run on pull requests. -
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 concurrentegc-memoryprocesses 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 plusfs.linkSync). Team sync seals every shared file with the team key plus an HMAC-SHA256 signature and rejects anything that does not open with both. - Session bus: event delivery is exactly-once across overlapping readers (compare-and-swap on the event cursor, v1.1.19, #1271), payloads are size-capped, claims are fail-fast, and a multi-process chaos harness covers SIGKILL lock recovery, contested claims, writer-death atomicity and per-sender ordering.
-
Credential path denylist:
egc-guardianblocks 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 --privilegedor host mounts,gh repo delete,gh api -X DELETE, andprisma migrate reset/--force-reset/db executereturn 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_learnwrite-target validation:target_fileis checked against protected paths and must stay inside the project root, closing a write-outside-sandbox path (v1.1.16, #1009). -
core.hooksPathbypass closed: git hook-path overrides are now matched case-insensitively, closing a DCO/CI bypass (v1.1.16, #1013). -
republish.ymlcommand 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
.dockerignorekeeps.git,.env, andnode_modulesout of the build context (v1.1.16, #1036, #1028).
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 remains an AI-tool-level concern: validate_content helps the AI notice it, but only the tool can refuse to act on it.
| Path | Risk | Protection |
|---|---|---|
mcp/servers/egc-guardian/src/index.ts (validate_command) |
High: gates shell execution | Reviewed on every change |
scripts/install.sh / scripts/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 |
Report privately through GitHub: github.com/Fmarzochi/EGC/security (Security Advisories). Do not open a public issue for a security finding.
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.