Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
282 lines
13 KiB
Markdown
282 lines
13 KiB
Markdown
<!--
|
|
Licensed to the Apache Software Foundation (ASF) under one
|
|
or more contributor license agreements. See the NOTICE file
|
|
distributed with this work for additional information
|
|
regarding copyright ownership. The ASF licenses this file
|
|
to you under the Apache License, Version 2.0 (the
|
|
"License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing,
|
|
software distributed under the License is distributed on an
|
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations
|
|
under the License.
|
|
-->
|
|
|
|
# Maka Security Policy
|
|
|
|
This document names the trust boundaries Maka treats as load-bearing,
|
|
the in-process safety nets it ships but does NOT treat as boundaries,
|
|
and how to report a vulnerability.
|
|
|
|
It is modeled after [hermes-agent's SECURITY.md][hermes-agent-sec] and
|
|
shares the same honesty principle: the only enforcement boundary
|
|
against an adversarial LLM is the operating system. Anything the
|
|
agent process screens internally is a heuristic operating on an
|
|
attacker-influenced string.
|
|
|
|
[hermes-agent-sec]: https://github.com/NousResearch/hermes-agent/security
|
|
|
|
## 1. Reporting a vulnerability
|
|
|
|
Report privately by emailing **security@maka.app** (or open a
|
|
private GitHub Security Advisory once that's enabled). Please do
|
|
NOT open public issues for security vulnerabilities.
|
|
|
|
Useful reports include:
|
|
|
|
- A concise description and severity assessment.
|
|
- The affected component, identified by file path and line range
|
|
(e.g. `apps/desktop/src/main/main.ts:120-145`).
|
|
- Environment details (`maka` version from the About page, OS,
|
|
Node / Electron version, recent commit SHA).
|
|
- A reproduction against `main` or the latest release.
|
|
- Which boundary in §2 you believe is crossed.
|
|
|
|
Read §2 and §3 before submitting. Reports that demonstrate limits
|
|
of an in-process heuristic this policy does not treat as a
|
|
boundary will be closed as out-of-scope, but they are still
|
|
welcome as ordinary issues or pull requests.
|
|
|
|
## 2. Trust model
|
|
|
|
Maka is a single-tenant personal desktop AI assistant. It runs as
|
|
the user's own OS account. The trust model has multiple layers,
|
|
and they are NOT equally load-bearing.
|
|
|
|
### 2.1 Definitions
|
|
|
|
- **Agent process.** The Electron main process and any code it
|
|
loads (`@maka/core`, `@maka/runtime`, `@maka/storage`,
|
|
`@maka/ui`, builtin tools, user skills).
|
|
- **Renderer process.** Electron's sandboxed renderer. Receives
|
|
data only through the preload IPC bridge in
|
|
`apps/desktop/src/preload/preload.ts`.
|
|
- **Permission engine.** `@maka/core/permission` evaluates each tool
|
|
category against the selected mode. `PERMISSION_MODES`,
|
|
`TOOL_CATEGORIES`, and `PERMISSION_POLICY` are the exact authority;
|
|
this policy does not duplicate their inventory.
|
|
- **Permission mode.** Per-session user setting. `ask` is the default;
|
|
its exact allow, prompt, and block decisions come from
|
|
`PERMISSION_POLICY`.
|
|
- **Input surface.** Any channel through which content enters the
|
|
agent's context: chat input, file reads, web fetches, enabled bot-platform
|
|
messages, tool results, and future external integrations.
|
|
- **Trust envelope.** The set of resources Maka inherits from the
|
|
user's OS account: filesystem, network, OS permissions
|
|
(Keychain / Microphone / Screen recording).
|
|
|
|
### 2.2 OS enforcement boundaries
|
|
|
|
**The only enforcement boundary against an adversarial LLM is the
|
|
operating system.** Nothing inside the agent process constitutes
|
|
containment — not the permission engine, not output redaction,
|
|
not URL allowlists, not query normalization, not the WebSearch
|
|
fail-closed chain.
|
|
|
|
Every in-process component that screens LLM output is a heuristic
|
|
operating on a string the LLM partially controlled. We ship them
|
|
because they are useful UX safety nets — they catch accidental
|
|
output and slow down adversarial output enough for a human to
|
|
notice — but we do not ship them as guarantees.
|
|
|
|
The OS user account is Maka's outer trust envelope. Inside that envelope,
|
|
Runtime Host also routes selected agent tool effects through OS-enforced child
|
|
process sandboxes when the active session has a restricted managed
|
|
`ExecutionBoundary`. On macOS and Linux, non-PTY Bash commands and the
|
|
filesystem worker run through Seatbelt and bubblewrap respectively. On
|
|
Windows, the AppContainer boundary currently covers the purpose-built
|
|
filesystem worker only; arbitrary-shell Bash is unavailable and fails closed
|
|
when the active profile requires a command sandbox.
|
|
|
|
This is not universal tool containment. Bypass boundaries and unrestricted,
|
|
disabled, or external profiles do not add a Maka-managed local sandbox;
|
|
external environments may supply their own isolation. Managed PTY Bash is
|
|
refused when the active profile requires sandboxing. Client-launched
|
|
integrated terminals are host PTYs outside the managed agent execution
|
|
boundary, and runtime or attachment resource reads do not execute through the
|
|
local filesystem worker. The standard `workspace-write` profile permits the
|
|
workspace roots and the configured temporary roots.
|
|
|
|
Seatbelt, bubblewrap, and AppContainer are OS enforcement mechanisms. The
|
|
in-process checks that decide whether to request or invoke them remain
|
|
heuristics, not containment. The exact product coverage and fail-closed
|
|
selection rules are documented in
|
|
[`packages/runtime/src/sandbox/README.md`](./packages/runtime/src/sandbox/README.md).
|
|
|
|
### 2.3 Boundaries we DO treat as load-bearing
|
|
|
|
1. **OS user account.** Tools run with the user's privileges. The
|
|
user is expected to run Maka as a non-admin account on systems
|
|
where that matters.
|
|
2. **Restricted managed tool sandboxes.** For the product surfaces listed in
|
|
§2.2, the active session `ExecutionBoundary` is compiled into a per-command
|
|
Seatbelt, bubblewrap, or AppContainer policy. Required enforcement fails
|
|
closed rather than silently retrying on the host. This boundary is limited
|
|
to the documented tool and platform matrix; it does not include bypass,
|
|
unrestricted, disabled, external, managed PTY, or integrated-terminal
|
|
execution.
|
|
3. **Credential-at-rest boundaries.** The provider credential store
|
|
writes `credentials.json` as versioned plaintext JSON under the
|
|
user's workspace directory. Its load-bearing boundary is the OS
|
|
user account plus filesystem controls: directory mode 0o700,
|
|
file mode 0o600, atomic writes, and no symlink/traversal escape.
|
|
Subscription OAuth tokens (Claude, Codex, GitHub Copilot, and xAI) live in
|
|
the same store: `credentials.json`
|
|
is the single authority every Runtime Host surface — Desktop, TUI, CLI —
|
|
reads and writes, under the same OS-account and 0o700/0o600 boundary
|
|
as other runtime credentials. Electron safeStorage is not part of
|
|
this boundary anymore. Pre-existing safeStorage-encrypted credential
|
|
or token files are not imported; users with only those copies must
|
|
re-authenticate.
|
|
4. **Renderer process sandbox + preload IPC bridge.** The
|
|
renderer cannot reach files, network, or shell directly. Every
|
|
IPC handler in `apps/desktop/src/main/main.ts` is the trust
|
|
boundary between renderer-controlled input and main-process
|
|
action. Renderer code is treated as semi-trusted: it can read
|
|
masked / sanitized data, but cleartext secrets never cross the
|
|
boundary in the main-to-renderer direction (see §4).
|
|
5. **Settings sensitive masking.** Tokens, API keys, and proxy
|
|
passwords are masked at the IPC store boundary
|
|
(`maskAppSettings` in `apps/desktop/src/main/settings-ipc-helpers.ts`).
|
|
Re-submitting the mask sentinel `••••••` is interpreted as
|
|
"keep current" by the merge logic; an empty string is
|
|
interpreted as an explicit clear. The Tavily API key follows the
|
|
same boundary.
|
|
6. **Network egress through user-configured proxies.** The
|
|
`network.proxy` settings drive Electron's session proxy. Tools
|
|
that bypass `proxiedFetch` (Tavily lives in main, uses
|
|
standard fetch) are individually audited.
|
|
|
|
### 2.4 Heuristics we ship but DO NOT treat as boundaries
|
|
|
|
These are valuable UX safety nets. Reports that limit one of them
|
|
are welcome as ordinary issues, not security advisories.
|
|
|
|
1. **Permission engine.** Prompts the user before
|
|
destructive / file-write / shell-unsafe / web-egress tools. A
|
|
determined adversarial LLM can still construct tool sequences that
|
|
the user accepts (social engineering). The OS is the boundary,
|
|
the engine is the UX.
|
|
2. **`redactSecrets()` output sanitizer.** Best-effort pattern
|
|
matcher applied at every text→DOM crossing
|
|
(`packages/ui/src/redact.ts`). Slows down accidental token
|
|
echo; does not catch novel formats.
|
|
3. **`normalizeSearchUrl()` URL allowlist.** Filters non-http(s)
|
|
URLs out of agent tool results before they reach a renderer
|
|
`<a href>`.
|
|
4. **`PermissionMode.ask`** as default. Mode names are UX controls,
|
|
not security boundaries. `ask` and `explore` establish restricted
|
|
managed profiles, while `bypass` explicitly does not promise a
|
|
Maka-managed local sandbox; the active `ExecutionBoundary` is the
|
|
execution authority.
|
|
5. **Modal-lifecycle contract test.** Catches the React
|
|
`useEffect`-before-`if (!open) return null` pattern that can
|
|
violate React hook ordering. Static analysis only.
|
|
6. **WebSearch fail-closed chain.** `invalid_query →
|
|
incognito_active → not_configured → invalid_credentials`. All
|
|
four return generalized Chinese copy without ever revealing
|
|
the API key, the raw provider body, or which gate tripped.
|
|
|
|
### 2.5 Privacy contract
|
|
|
|
Beyond security, Maka treats the following as user-facing
|
|
privacy commitments:
|
|
|
|
- **Workspace state stays local.** Session messages, tool
|
|
results, telemetry, settings are stored under
|
|
`app.getPath('userData')`. Cloud sync is not shipped.
|
|
- **Tool query strings are NEVER logged.** The WebSearch tool's
|
|
`argsSummary` is scrubbed at the `recordToolInvocation` hook.
|
|
- **Incognito context.** When the workspace privacy context
|
|
reports `incognitoActive: true`, the WebSearch tool fails
|
|
closed before any network call. Main composition and focused
|
|
consumer tests own the full enforcement inventory.
|
|
- **Token boundary.** Cleartext API keys / OAuth tokens / bot
|
|
tokens NEVER cross the main→renderer IPC boundary.
|
|
`apps/desktop/src/main/__tests__/web-search-boundary.test.ts` and
|
|
`claude-subscription-ipc-boundary.test.ts` enforce this for Tavily
|
|
and Claude subscription credentials.
|
|
|
|
## 3. Scope of vulnerability reports
|
|
|
|
A report is in scope when it demonstrates that a load-bearing
|
|
boundary in §2.3 was crossed. Examples:
|
|
|
|
- A renderer-supplied IPC payload reaches the filesystem outside
|
|
the workspace or beyond an explicit user approval.
|
|
- `credentials.json` is written outside the workspace credential
|
|
path, through a symlink/traversal escape, or with POSIX permissions
|
|
looser than the 0o700/0o600 boundary.
|
|
- A production OAuth path writes or requires a safeStorage-encrypted
|
|
token copy again. `credentials.json` is the single documented token
|
|
authority; there is no safeStorage legacy import fallback.
|
|
- A cleartext secret crosses main→renderer IPC under any
|
|
circumstance (including error paths, settings preview, IPC
|
|
result envelopes, log lines).
|
|
- A tool intended to be permission-gated bypasses the
|
|
PermissionEngine.
|
|
- A non-PTY Bash command or local-path filesystem operation under a
|
|
restricted managed boundary escapes its effective Seatbelt,
|
|
bubblewrap, or supported AppContainer policy, or silently falls
|
|
back to host execution when required enforcement is unavailable.
|
|
|
|
Reports against the §2.4 heuristics are out of scope as security
|
|
advisories. They are welcome as ordinary issues or pull requests.
|
|
|
|
## 4. Token boundary policy
|
|
|
|
This policy is explicit because it is the most frequent source of
|
|
misunderstanding.
|
|
|
|
**Cleartext bot / API / OAuth tokens cross the
|
|
main↔renderer IPC boundary in exactly one direction: the renderer
|
|
SUBMITS a token when the user pastes it into a Settings input.
|
|
The main process NEVER returns the cleartext token to the
|
|
renderer.**
|
|
|
|
Reads of `settings:get` return the bullet-mask sentinel `••••••`
|
|
for all sensitive fields. The renderer can detect "has a stored
|
|
value" vs "empty" by comparing against the sentinel, but cannot
|
|
ever see the value. An update payload that contains exactly the
|
|
sentinel is interpreted by
|
|
`mergeWebSearchSettings` (and its peers in `mergeSettings`) as
|
|
"keep current", so a round-trip through `settings:get →
|
|
settings:update` cannot accidentally overwrite the stored value.
|
|
|
|
Credential-test requests may submit an unsaved cleartext token so the
|
|
user can verify it before saving. The main process accepts it for that
|
|
single request and does not echo it in the response.
|
|
|
|
The static-analysis contract tests for this policy:
|
|
- `apps/desktop/src/main/__tests__/web-search-boundary.test.ts`
|
|
- `apps/desktop/src/main/__tests__/claude-subscription-ipc-boundary.test.ts`
|
|
|
|
## 5. Versioning
|
|
|
|
This policy applies to the current `main` branch and the latest
|
|
release. Older releases are not under active security maintenance.
|
|
If we ever cut a long-term support release we will say so in the
|
|
release notes.
|
|
|
|
## 6. Acknowledgement
|
|
|
|
The structure and several phrases of this document are adapted
|
|
from the [hermes-agent SECURITY.md][hermes-agent-sec]. Thanks to the
|
|
Nous Research team for setting the bar on honest agent security
|
|
framing.
|