-
-
Notifications
You must be signed in to change notification settings - Fork 42
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 |
-
Supply chain: dependencies locked via
package-lock.json, Dependabot monitors for vulnerabilities,dependency-review.ymlblocks PRs introducing high-severity dependencies,npm auditruns on every push. -
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). -
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. -
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. -
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). -
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 is an AI-tool-level concern, not addressable at the EGC layer.
| 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 |
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.