Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
# Security Policy
`akcli` parses **untrusted binary and text input** — Altium `.SchDoc`/`.SchLib`/`.PcbDoc` OLE2
containers and KiCad S-expression files — and can **write** KiCad schematics. The threat model and
the enforced safety limits below are first-class design requirements, not afterthoughts.
## Reporting a vulnerability
Please report security issues through GitHub's **private vulnerability reporting** on this repository
(the **Security** tab → *Report a vulnerability*). If private reporting is not enabled, open a GitHub
issue and prefix the title with `security:`. Please do not publicly disclose an unfixed vulnerability
before a fix is available. There is no bug-bounty program.
## Threat model
The core assumption: **input files are hostile.** A `.SchDoc` may come from an email attachment, a
shared drive, or an AI agent acting on a remote URL. A KiCad file or a JSON op-list may be attacker-
controlled. The parser and writer therefore treat every byte as adversarial.
Attack surfaces and the threats we defend against:
1. **Malformed / weaponized OLE2 (CFBF) containers.** Crafted FAT/miniFAT chains causing infinite
loops; out-of-bounds sector references; bogus sector-shift / mini-cutoff header fields used as
allocation bombs; cyclic or self-referential red-black directory trees; DIFAT spillover.
2. **Malformed KiCad S-expressions.** Deeply nested parentheses to exhaust the stack (recursion →
uncatchable `SIGSEGV`); multi-megabyte atoms or unterminated quotes to exhaust memory; pathological
node counts.
3. **Resource exhaustion (DoS).** Oversized files, decompression/decoding bombs, runaway record counts.
4. **Path traversal.** A malicious `lib_id`, config `[paths]`, or op-list `target_file` path that tries
to escape a project root, follow symlinks, or expand environment variables to read or clobber files
outside the intended directory.
5. **Subprocess / command injection.** Invoking `kicad-cli` with attacker-influenced arguments.
6. **Destructive writes.** A crash or a bad op-list corrupting the user's real `.kicad_sch`.
Out of scope: vulnerabilities in third-party tools you install yourself (KiCad, Altium), the security
of files you *intend* to overwrite with `--apply`, and protecting against a malicious local user who
already has your filesystem privileges.
## Enforced safety limits (`safety.py`)
All limits live in one module (`akcli.safety`) and are applied at every read/write boundary.
A violation raises a **structured** `AkcliError` with a stable error code and a non-zero exit code — a
raw traceback never reaches the agent unless `--debug` is passed.
| Limit constant | Bounds | Defeats |
|---|---|---|
| `MAX_FILE_BYTES` | total input file size | oversized-file DoS |
| `MAX_SECTORS` | OLE2 FAT/miniFAT sectors walked (with a seen-set) | FAT cycles, sector-chain bombs |
| `MAX_RECORDS` | Altium records decoded per stream | record-count exhaustion |
| `MAX_DIR_ENTRIES` | CFBF directory-tree entries visited | directory-tree cycles/explosion |
| `MAX_DECODED_BYTES` | bytes materialized from any stream | decode/allocation bombs |
| `MAX_SEXPR_DEPTH` | S-expression nesting depth | stack-exhaustion `SIGSEGV` |
| `MAX_ATOM_BYTES` | single S-expression atom size | giant-atom memory bombs |
| `MAX_NODES` | total S-expression nodes | node-count exhaustion |
Additional structural defenses:
- **Iterative parsers only.** The S-expression parser uses an explicit stack; calling
`sys.setrecursionlimit` is banned. Cycle detection (seen-sets) guards every OLE2 FAT/miniFAT and
directory-tree walk.
- **Header validation before allocation.** Sector-shift must be `{9, 12}`, mini-sector-shift `6`,
mini-cutoff `4096`; the file must be ≥ 512 bytes; the DIFAT spillover chain is walked under the
header-declared count, a cycle set, and the global sector cap (`ALTIUM_FAT_CYCLE` /
`ALTIUM_ALLOC_GUARD` / `ALTIUM_MALFORMED` on hostile input). No untrusted header field is used to
size an allocation unchecked.
- **Time and memory budgets** are exercised by `test_fuzz_safety.py` against a corpus of malformed
inputs (FAT cycle, OOB sector, bogus sector-shift, huge `ndifat`, mini-cutoff bomb, truncated header,
missing root, deeply nested S-expr, 10 MB atom, unterminated quote, symlinked lib path). Each must
raise a structured error within budget.
### Path safety — `safety.safe_path(base, candidate)`
- `realpath`s both base and candidate and **rejects any path that escapes `base`** or traverses a
symlink out of the allowlisted root → `PATH_OUTSIDE_ROOT`.
- **Never expands environment variables or `~`** from untrusted file contents.
- Applied to `lib_id` symbol-source resolution, config `[paths]`, and op-list `target_file` paths.
### Subprocess safety — `safety.run_subprocess(argv, timeout, maxout)`
- `shell=False`, **absolute executable path**, an explicit `--` before any file path, a hard
**timeout** (`KICAD_CLI_TIMEOUT`), and a captured-output cap. No string is ever passed to a shell.
- `kicad-cli` is invoked **without** `--exit-code-violations` (its ERC exits 0 even with violations);
its absence is non-fatal (`KICAD_CLI_MISSING`) because the pure-Python verifier is primary.
### Write safety — `safety.atomic_write_with_backup(...)`
KiCad writes are **default dry-run**; an explicit `--apply` is required. On apply:
1. Snapshot the original (timestamped backup).
2. Write to a temp file **in the same directory**, then `fsync`.
3. Re-parse the temp file and run the pure-Python connectivity verifier on it.
4. Only on a clean verify, `os.replace` (atomic) into place — otherwise abort, leaving the original
untouched (`VERIFY_FAILED`).
An mtime/hash optimistic lock detects concurrent external edits before replacing.
### Altium files are never written
Every Altium reader is strictly read-only, and no code path drives an external EDA process (the
Windows live-bridge scaffold was deleted in 0.17). The only write target is `.kicad_sch`, through the
atomic pipeline above.
## Supported versions
Pre-1.0: only the latest commit on `main` (and, by extension, the most recently tagged release)
receives fixes. Once 1.0 ships, the latest minor series will be supported.
2126c3284This file is pinned to the selected release. Download the original to open it in your design tool.