diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..1d6c4c1f --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,38 @@ +name: Documentation + +on: + push: + branches: [main] + paths: + - "book/**" + - "docs/**" + - "README.md" + - "SECURITY.md" + - ".github/workflows/docs.yml" + pull_request: + paths: + - "book/**" + - "docs/**" + - "README.md" + - "SECURITY.md" + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: docs-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: mdBook build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install mdBook + run: cargo install mdbook --version 0.4.52 --locked + - name: Build documentation + run: mdbook build book diff --git a/.gitignore b/.gitignore index 12941800..9707ed4d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ target/ /log /secrets /temp +/book/output diff --git a/README.md b/README.md index f0ec0517..6a7b2a82 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,14 @@ Installers for macOS, Windows, Linux and the web are attached to each [GitHub re > [!IMPORTANT] > **Status:** PhotoCraft is in early alpha. The core editing workflow is here, and we're working toward full Photoshop parity milestone by milestone (see [`docs/roadmap.md`](docs/roadmap.md)). Progress is measured, not guessed: `cargo xtask parity` checks every item in Photoshop's menu tree against the live command registry and writes [`docs/parity.md`](docs/parity.md). Expect rough edges, and please file issues. You can also tell us what broke on [Discord](https://discord.gg/artcraft). +## Documentation + +Developer, architecture, automation, format, and security documentation is maintained in the [PhotoCraft documentation book](book/). + +## Security + +Security architecture, threat modeling, parser hardening, fuzzing, and vulnerability reporting are covered in the [security documentation](book/src/security/) and the repository [security policy](SECURITY.md). + ## The Crafting Apps PhotoCraft is one of the **Crafting Apps**: free, open-source creative tools from the diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..b7186f52 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +PhotoCraft processes complex document and image formats and exposes CLI, JSON control, and MCP automation. Treat files, paths, metadata, automation requests, and command parameters as potentially malicious. + +## Reporting a vulnerability + +Please give the repository maintainers a reasonable opportunity to investigate and coordinate a fix before publishing exploitable details, proof-of-concept code, or weaponized files. + +This repository does not currently document a dedicated private security email or private reporting channel. Contact the repository maintainers through the repository owner/maintainer channels already listed in the project, provide only a minimal non-exploitable summary in public, and ask for an appropriate private transfer method before sharing sensitive details. Do not open a public issue containing a working exploit, secret, sensitive crash dump, or malicious attachment. + +If no private channel can be established, withhold weaponized material and report the smallest safe description that allows maintainers to make contact. + +## What to include + +A useful report includes: + +- affected PhotoCraft version, commit, or release artifact; +- operating system, architecture, build profile, and relevant feature flags; +- the affected component or path; +- security impact and required attacker access; +- exact reproduction steps; +- expected behavior and actual behavior; +- crash logs, backtraces, sanitizer output, or resource measurements when applicable; +- whether the problem reproduces from a clean checkout; +- any known workaround or containment. + +Remove credentials, personal paths, private document content, and unrelated system information from logs. + +## Malicious test files + +Do not attach a potentially harmful or non-redistributable file to a public issue. Initially provide its format, size, cryptographic hash, observed effect, provenance category (for example, synthetic or fuzz-generated), and the command needed to reproduce the problem. Coordinate a private transfer method with maintainers before sending the sample. + +Test files accepted into the repository must be minimized, free of personal or proprietary data, legally redistributable, and documented with the expected failure behavior. A fixed parser defect should receive a permanent regression test whenever safe and practical. + +## Scope + +Security reports may cover: + +- PSD/PSB, raster, `.pcraft`, ICC, LUT, pattern, and embedded-object parsing; +- decompression bombs, oversized dimensions, integer overflow, memory exhaustion, recursion, or hangs; +- import/export and filesystem path handling, including traversal, symlinks, and unsafe overwrite behavior; +- engine command validation and serialization/deserialization; +- the desktop control server, headless JSON server, MCP server, and MCP-to-GUI bridge; +- authentication, authorization/capabilities, request limits, batch limits, and connection exhaustion; +- GPU resource validation or driver-facing input handling; +- dependencies, CI, packaging, signing, checksums, and release artifact integrity. + +General bugs without a security impact should use the project's normal issue and contribution process. + +## Current limitations + +The current loopback control channel has no authentication or capability model. Loopback binding is not an authorization boundary. Automation file operations are not restricted to configured workspace roots, and explicit request, batch, and connection limits are not yet implemented. See the [security documentation](book/src/security/overview.md) for the source-backed status and hardening roadmap. diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 00000000..804f9422 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,31 @@ +[book] +authors = ["PhotoCraft contributors"] +language = "en" +multilingual = false +src = "src" +title = "PhotoCraft Documentation" + +[build] +build-dir = "output" +create-missing = false + +[output.html] +git-repository-icon = "fa-github" +git-repository-url = "https://github.com/storytold/photocraft" +edit-url-template = "https://github.com/storytold/photocraft/edit/main/book/{path}" +preferred-dark-theme = "navy" +smart-punctuation = true + +[output.html.fold] +enable = true +level = 1 + +[output.html.search] +enable = true +limit-results = 30 +use-boolean-and = true +boost-title = 2 +boost-hierarchy = 1 +boost-paragraph = 1 +expand = true +heading-split-level = 3 diff --git a/book/src/README.md b/book/src/README.md new file mode 100644 index 00000000..f587a109 --- /dev/null +++ b/book/src/README.md @@ -0,0 +1,34 @@ +# PhotoCraft Documentation + +PhotoCraft is a native image editor written in Rust. This book is the maintained entry point for installation, architecture, formats, automation, development, and security documentation. + +The repository's existing `docs/` directory remains authoritative for detailed design notes, parity reports, UI conventions, release procedures, and the complete control-protocol method reference. The book links to those documents instead of copying them. + +## Start here + +- Users and evaluators: [Installation](getting-started/installation.md) +- Contributors: [Building](getting-started/building.md) and [Contributing](getting-started/contributing.md) +- Architecture work: [Architecture overview](architecture/overview.md) +- Agents and automation clients: [Automation overview](automation/overview.md) +- Security-sensitive work: [Security overview](security/overview.md) and [Threat model](security/threat-model.md) + +## Documentation for coding agents + +Coding agents should read the following in order: + +1. [`AGENTS.md`](https://github.com/storytold/photocraft/blob/main/AGENTS.md) for repository rules and verification gates. +2. [`README.md`](https://github.com/storytold/photocraft/blob/main/README.md) for product scope and current entry points. +3. [`book/src/architecture/`](architecture/overview.md) for crate boundaries and command flow. +4. [`book/src/automation/`](automation/overview.md) before driving the CLI, control protocol, or MCP server. +5. [`book/src/security/`](security/overview.md) before changing a parser, file I/O, automation, MCP, the control server, command execution, or serialization/deserialization. + +Agents must treat document bytes, paths, command parameters, and automation messages as untrusted input. A page that describes a proposed control does not mean that control is implemented. + +## Status language + +Security pages use four labels consistently: + +- **Implemented:** behavior confirmed in current source or tests. +- **Known limitation:** an exposed risk or missing control in current source. +- **Proposed:** a design direction that is not enforced by the current build. +- **Future hardening:** useful follow-up work without an accepted implementation yet. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 00000000..d3ef25c8 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,56 @@ +# Summary + +[PhotoCraft Documentation](README.md) + +# Getting started + +- [Installation](getting-started/installation.md) +- [Building](getting-started/building.md) +- [Contributing](getting-started/contributing.md) + +# Architecture + +- [Overview](architecture/overview.md) +- [Crates and dependency layers](architecture/crates.md) +- [Command engine](architecture/command-engine.md) +- [Rendering](architecture/rendering.md) +- [Automation architecture](architecture/automation.md) + +# Formats + +- [Overview](formats/overview.md) +- [PSD and PSB](formats/psd-psb.md) +- [Raster formats](formats/raster-formats.md) +- [The `.pcraft` format](formats/pcraft.md) + +# Automation + +- [Overview](automation/overview.md) +- [Command-line interface](automation/cli.md) +- [Control protocol](automation/control-protocol.md) +- [MCP](automation/mcp.md) +- [Automation security model](automation/security-model.md) + +# Security + +- [Overview](security/overview.md) +- [Threat model](security/threat-model.md) +- [Trust boundaries](security/trust-boundaries.md) +- [Secure file handling](security/secure-file-handling.md) +- [Parser hardening](security/parser-hardening.md) +- [Fuzzing and regression testing](security/fuzzing.md) +- [Automation security](security/automation-security.md) +- [Vulnerability reporting](security/vulnerability-reporting.md) + +# Development + +- [Testing](development/testing.md) +- [Fuzzing](development/fuzzing.md) +- [Dependencies](development/dependencies.md) +- [Release security](development/release-security.md) + +# Project + +- [Roadmap](project/roadmap.md) +- [Credits](project/credits.md) +- [Licenses](project/licenses.md) diff --git a/book/src/architecture/automation.md b/book/src/architecture/automation.md new file mode 100644 index 00000000..1930d90a --- /dev/null +++ b/book/src/architecture/automation.md @@ -0,0 +1,19 @@ +# Automation architecture + +PhotoCraft exposes the same engine through several transports: + +```text +photocraft-cli commands/run/batch + | +MCP stdio ---+--> photocraft-automation::Headless --> engine Session + +MCP bridge --> BridgeClient --> 127.0.0.1 control server --> UI ControlRequest + +JSON-lines stdio/TCP --> rpc::respond --> Headless::handle --> engine Session +``` + +Headless file operations are implemented in `crates/automation/src/files.rs`; RPC dispatch is in `crates/automation/src/rpc.rs`; MCP tools are registered in `crates/automation/src/server.rs`; and the live client is in `crates/automation/src/bridge.rs`. The desktop TCP listener is `apps/photocraft/src/control_server.rs`. + +The control and headless TCP listeners are restricted to loopback addresses. Stdio MCP avoids a listening network socket, but the MCP process still acts with the filesystem permissions of the user who launched it. + +Loopback is not authentication. Current transports do not implement authentication, capability scopes, allowed filesystem roots, request-byte limits, batch-step limits, or connection limits. See the [Automation security model](../automation/security-model.md) and [Automation security](../security/automation-security.md) before exposing or extending these surfaces. diff --git a/book/src/architecture/command-engine.md b/book/src/architecture/command-engine.md new file mode 100644 index 00000000..0326164a --- /dev/null +++ b/book/src/architecture/command-engine.md @@ -0,0 +1,33 @@ +# Command engine + +Every user-visible editing action is represented by a command specification in `crates/engine/src/*_cmds.rs` and registered by `crates/engine/src/commands.rs`. A specification supplies an ID, label, menu path, shortcut, parameter documentation, enabled predicate, and `run` implementation. + +```text +caller + | + | command id + JSON parameters + v +command registry -- enabled/parameter checks --> command run closure + | + v + Session + one history step +``` + +The desktop UI, CLI, control protocol, and MCP tools all dispatch into this registry. Pure view/window state is the exception and is handled by the UI shell's command table. + +## Input contract + +Command parameters and current document state are untrusted at the command boundary. Implementations must return `Err` for missing, wrong-type, out-of-range, or contradictory parameters. Values derived from input must not reach unchecked indexing, division, allocation sizing, or panic paths. + +The ignored `crates/engine/tests/panic_hunt.rs` integration test exercises commands with adversarial parameters. A changed command also needs focused graceful-failure tests and should be verified with: + +```sh +cargo test -p photocraft-engine +cargo test -p photocraft-engine --test panic_hunt -- --ignored +``` + +The exact command-creation checklist is maintained in the existing [contribution guide](https://github.com/storytold/photocraft/blob/main/docs/contributing.md). + +## Security boundary note + +The registry validates command behavior, but current automation transports do not provide per-command capabilities. An MCP or control client that reaches command dispatch can request any exposed command whose runtime `enabled` predicate allows it. Capability enforcement is [proposed automation hardening](../security/automation-security.md), not an implemented control. diff --git a/book/src/architecture/crates.md b/book/src/architecture/crates.md new file mode 100644 index 00000000..c423cf2e --- /dev/null +++ b/book/src/architecture/crates.md @@ -0,0 +1,25 @@ +# Crates and dependency layers + +The layer numbers below describe allowed dependency direction, not runtime privilege. A crate may depend only on lower layers. Standalone format crates remain independent of other workspace crates where specified. + +| Layer | Crates | Responsibility | +|---|---|---| +| L0 | `geom`, `cms`, `color`, `raster` | Geometry, ICC processing, color/blend math, pixel formats, and copy-on-write tiles | +| L0 standalone | `psd`, `codecs` | PSD/PSB and flat raster format parsing/encoding | +| L1 | `doc` | Pure-data documents, layers, masks, effects, adjustments, text, vectors, and smart objects | +| L2 | `ops`, `paint`, `algo`, `text`, `vector` | History operations, painting, imaging algorithms, type, paths, and shapes | +| L3 | `compose`, `gpu`, `format` | CPU composition, wgpu composition, and `.pcraft` persistence | +| L4 | `io` | Import/export between the document model and external formats | +| L5 | `engine` | `Session`, command registry, validation, history integration, and inspection | +| L6 | `ui-egui`, `automation` | egui shell, headless automation, MCP, and the live bridge | +| Test support | `testkit` | Shared test helpers | + +Workspace applications are: + +| Application | Path | Purpose | +|---|---|---| +| PhotoCraft desktop | `apps/photocraft` | eframe/wgpu application and loopback control server | +| PhotoCraft CLI | `apps/photocraft-cli` | Convert, inspect, run, batch, JSON-lines serve, and MCP | +| PhotoCraft web | `apps/photocraft-web` | The UI and engine compiled for WebAssembly | + +New crates must be registered in `xtask/src/layers.rs`. Nothing below `ui-egui` may depend on egui, eframe, winit, or rfd. The `psd`, `codecs`, and `cms` crates have additional independence requirements documented in [`AGENTS.md`](https://github.com/storytold/photocraft/blob/main/AGENTS.md). diff --git a/book/src/architecture/overview.md b/book/src/architecture/overview.md new file mode 100644 index 00000000..88a1fd79 --- /dev/null +++ b/book/src/architecture/overview.md @@ -0,0 +1,33 @@ +# Architecture overview + +PhotoCraft separates the document engine from presentation and transport. The desktop UI, CLI, JSON control channel, and MCP server reach editing behavior through the command registry rather than maintaining separate implementations. + +```text +desktop UI CLI JSON control MCP + | | | | + +-------------+--------------+--------------+ + | + command registry + | + engine Session + | + document model + operations + compositors + | + PSD / raster / .pcraft I/O +``` + +The enforced dependency direction runs from format-independent foundation crates through the document and operation layers to composition, I/O, the engine, and finally UI/automation. `cargo xtask layers` checks this rule. + +The complete design narrative and dependency diagram remain in [`docs/architecture.md`](https://github.com/storytold/photocraft/blob/main/docs/architecture.md). That document contains historical design material as well as implemented architecture; confirm behavior in current source when security or compatibility depends on it. + +## Core properties + +- `photocraft-doc` holds the pure-data document model. +- `photocraft-engine::Session` owns open documents, active state, history, and command dispatch. +- `photocraft-compose` is the CPU reference compositor; `photocraft-gpu` accelerates supported interactive paths. +- `photocraft-io` maps between documents and PSD or flat formats. +- `photocraft-format` reads and writes the native `.pcraft` bundle. +- `photocraft-ui-egui` is a thin shell over engine commands. +- `photocraft-automation` exposes the headless engine and the live-GUI bridge. + +See [Crates and dependency layers](crates.md) for the complete workspace map. diff --git a/book/src/architecture/rendering.md b/book/src/architecture/rendering.md new file mode 100644 index 00000000..84a2b884 --- /dev/null +++ b/book/src/architecture/rendering.md @@ -0,0 +1,23 @@ +# Rendering + +PhotoCraft has two compositors with distinct roles: + +- `photocraft-compose` is the CPU compositor and correctness oracle used by export and tests. +- `photocraft-gpu` plans and executes supported canvas composition through wgpu. + +The interactive canvas uses the GPU path when the planner can represent the document. Unsupported cases fall back to CPU composition. Current documentation identifies Multichannel documents and regions above the GPU texture limit as fallback cases. + +Pixel storage in `photocraft-raster` supports runtime sample depth rather than a public 8-bit-only path. Color mode and profiles are document data; color conversion belongs in `photocraft-cms` rather than being treated as implicit sRGB. + +## Verification + +GPU changes require parity checks against the CPU oracle. UI changes require an actual rendered image, using either the offscreen snapshot example or a control-channel screenshot: + +```sh +cargo run -p photocraft-ui-egui --example snapshot +cargo test -p photocraft-gpu +``` + +Performance-sensitive work should avoid full-surface work per frame, operate per tile, skip empty tiles, and cache by revision. The current commands and benchmark entry points are listed in [`docs/development.md`](https://github.com/storytold/photocraft/blob/main/docs/development.md). + +GPU processing is not a security sandbox. Dimensions, decoded buffers, shaders, and command parameters must be validated before resource creation or dispatch. diff --git a/book/src/automation/cli.md b/book/src/automation/cli.md new file mode 100644 index 00000000..bccac100 --- /dev/null +++ b/book/src/automation/cli.md @@ -0,0 +1,26 @@ +# Command-line interface + +`apps/photocraft-cli` provides the following current subcommands: + +| Command | Purpose | +|---|---| +| `convert` | Open one document and export it to another format | +| `info` | Print document size, mode, depth, and layer information as JSON | +| `run` | Open or create a document, execute engine commands, and optionally save | +| `batch` | Apply an action list to files in one input directory | +| `droplet` | Run a PhotoCraft droplet against files or folders | +| `commands` | List command IDs and parameter documentation | +| `mcp` | Start the MCP server on stdio, optionally bridged to the desktop app | +| `serve` | Start the JSON-lines headless server on stdio or loopback TCP | + +Use `photocraft-cli --help` as the executable source of truth. A typical bounded command run is: + +```sh +photocraft-cli run input.psd \ + --cmd image.adjustments.invert \ + --out output.png +``` + +Paths supplied to the current CLI are ordinary operating-system paths. The CLI does not restrict them to a workspace root. Batch output creates the requested output directory, and save operations may create parent directories. Run untrusted action files only in an appropriately isolated account or environment. + +CLI parsing reports malformed flags and JSON as errors, but it is not a security policy layer. Engine commands remain responsible for validating their own parameters. diff --git a/book/src/automation/control-protocol.md b/book/src/automation/control-protocol.md new file mode 100644 index 00000000..a00ea526 --- /dev/null +++ b/book/src/automation/control-protocol.md @@ -0,0 +1,23 @@ +# Control protocol + +The desktop control protocol is newline-delimited JSON over a TCP listener bound to `127.0.0.1`. Each request carries `id`, `method`, and optional `params`; each reply includes the matching `id` and either a result or error. + +```json +{"id":1,"method":"ui.inspect","params":{}} +``` + +The current method catalog includes engine execution and discovery, application open/save operations, UI inspection and input, screenshots, and document/control helpers. The authoritative method and parameter tables are maintained in [`docs/control-protocol.md`](https://github.com/storytold/photocraft/blob/main/docs/control-protocol.md). + +Implementation is split across: + +- `apps/photocraft/src/control_server.rs`: loopback TCP transport; +- `crates/ui-egui/src/control.rs`: live application handlers; +- `crates/automation/src/bridge.rs`: MCP-to-GUI client. + +## Current security posture + +**Implemented:** the desktop server binds to IPv4 loopback, the bridge accepts only loopback-style addresses, and request handlers use a 60-second response timeout. + +**Known limitations:** there is no authentication handshake, encryption, client identity, per-method capability check, request-line byte limit, connection limit, or bounded worker pool. The listener creates one thread per accepted connection. Any local process able to connect can attempt exposed operations with PhotoCraft's user permissions. + +Enable `--control` only for the duration of a trusted local automation session. See [Automation security](../security/automation-security.md) for the proposed gateway model. diff --git a/book/src/automation/mcp.md b/book/src/automation/mcp.md new file mode 100644 index 00000000..ed30c8d4 --- /dev/null +++ b/book/src/automation/mcp.md @@ -0,0 +1,28 @@ +# MCP + +`photocraft-automation` implements an MCP server over stdio with two backends: + +- **Headless:** an in-process `Headless` session owns the engine and performs file I/O. +- **Bridge:** `BridgeClient` forwards tools to a desktop application started with `photocraft --control `. + +Start the headless server with: + +```sh +photocraft-cli mcp +``` + +Bridge to a running desktop application with: + +```sh +photocraft-cli mcp --bridge 127.0.0.1:7878 +``` + +Tools cover session/document operations, command discovery and execution, batching, and—when bridged—UI inspection and control. Tool schemas improve correctness but are not authorization boundaries. + +## Security notes + +Stdio MCP does not open a network listener. Its client can still invoke file operations and engine commands using the MCP process's operating-system permissions. Bridge mode additionally inherits the unauthenticated control-channel limitation. + +The current server does not issue scoped sessions or distinguish trusted tools from high-impact tools. A connected client is not restricted to document-only access. Do not expose the stdio transport through an untrusted broker, shell, or remote service without adding an external policy boundary. + +Capability-scoped MCP access is [proposed](../security/automation-security.md), not implemented. diff --git a/book/src/automation/overview.md b/book/src/automation/overview.md new file mode 100644 index 00000000..c684364c --- /dev/null +++ b/book/src/automation/overview.md @@ -0,0 +1,21 @@ +# Automation overview + +PhotoCraft automation has three entry styles: + +1. `photocraft-cli` runs finite convert, inspect, command, batch, and droplet jobs. +2. `photocraft-cli serve` keeps a headless session alive over JSON lines on stdio or loopback TCP. +3. `photocraft-cli mcp` exposes MCP tools over stdio, either against an in-process headless session or through a bridge to the desktop control server. + +The desktop application can start a loopback JSON control server with `photocraft --control `. It exposes engine and UI operations, including pointer/type input and screenshots. + +All editing routes eventually reach the command registry. Transport behavior is not equivalent, however: only the live bridge can use UI-specific methods, and file operations execute in the process that owns the selected backend. + +## Safe operating assumptions + +- Prefer one-shot CLI commands or stdio MCP when a TCP listener is unnecessary. +- Run automation with the least-privileged operating-system account practical. +- Do not enable the desktop control port persistently. +- Do not send secrets in command parameters, filenames, document metadata, or logs. +- Treat tool descriptions and document content as untrusted; they do not grant authority. + +Current security limitations are summarized in [Automation security model](security-model.md). diff --git a/book/src/automation/security-model.md b/book/src/automation/security-model.md new file mode 100644 index 00000000..29aeb5e4 --- /dev/null +++ b/book/src/automation/security-model.md @@ -0,0 +1,37 @@ +# Automation security model + +Automation requests are untrusted input with potential effects beyond the active document. Opening, saving, rendering to a path, preferences changes, application control, pointer/type injection, and arbitrary engine commands have different risk levels but currently share broad transport access. + +| Control | Current status | +|---|---| +| Loopback-only TCP binding | **Implemented** for desktop control and headless TCP serve | +| Stdio transport | **Implemented** for MCP and headless JSON lines | +| Command-level error handling | **Implemented**, with command-specific tests and `panic_hunt` coverage | +| Control-channel authentication | **Known limitation:** absent | +| MCP capability scopes | **Known limitation:** absent | +| Allowed read/write roots | **Known limitation:** absent | +| Symlink-safe capability filesystem | **Known limitation:** absent | +| Request-byte and JSON-depth limits | **Known limitation:** absent at the transport boundary | +| Batch-step limit | **Known limitation:** absent | +| Connection/worker limit | **Known limitation:** absent for TCP servers | +| Security audit events | **Proposed** | + +## Proposed gateway + +```text +MCP or control client + | + authenticated session + v + security gateway + - capabilities + - path handles/roots + - request budgets + - command policy + - audit events + | + v + command engine / UI shell +``` + +A token alone would identify possession of a secret but would not limit what that client can do. The proposed design combines a random per-launch credential with explicit capabilities, filesystem handles, resource budgets, and command policy. Until those controls land, process isolation and least-privileged execution are the practical containment mechanisms. diff --git a/book/src/development/dependencies.md b/book/src/development/dependencies.md new file mode 100644 index 00000000..3a06fdb9 --- /dev/null +++ b/book/src/development/dependencies.md @@ -0,0 +1,18 @@ +# Dependencies + +Cargo dependencies are pinned through `Cargo.lock` and exercised by the normal workspace build, test, clippy, and WebAssembly jobs. Parser and GPU dependencies deserve additional review because they process attacker-controlled data or interface with native drivers. + +## Current repository state + +No `cargo-deny` policy file, `cargo-audit` job, or equivalent dependency-advisory workflow is present in the current tree. Do not describe dependency auditing as implemented until configuration and CI evidence land. + +## Proposed checks + +```sh +cargo audit +cargo deny check +``` + +A future `deny.toml` should be reviewed by maintainers and encode accepted licenses, advisory policy, source rules, and duplicate-version policy. Advisory exceptions need an owner, rationale, compensating control, and expiration/review date. CI tools and GitHub Actions should also be version-pinned and reviewed as dependencies. + +Dependency changes should document why a crate is needed, whether it processes untrusted input, unsafe/native code exposure, supported targets, maintenance status, and license compatibility. Avoid replacing source review with a scanner result. diff --git a/book/src/development/fuzzing.md b/book/src/development/fuzzing.md new file mode 100644 index 00000000..bd095792 --- /dev/null +++ b/book/src/development/fuzzing.md @@ -0,0 +1,16 @@ +# Fuzzing + +Fuzzing procedures, current targets, security properties, and corpus rules are maintained in [Fuzzing and regression testing](../security/fuzzing.md). + +The existing fuzz workspaces are intentionally separate from the main Cargo workspace because `cargo-fuzz` uses nightly Rust and sanitizer instrumentation. Example: + +```sh +rustup toolchain install nightly +cargo install cargo-fuzz --locked +cd crates/codecs +cargo +nightly fuzz run decode_any +``` + +Reproduce a crash, minimize it, add a deterministic regression test, and then fix it. A time-limited fuzz run with no findings is evidence of that run only; it is not proof that a parser is secure. + +The main GitHub Actions workflow currently runs fuzzing only when manually dispatched with the `fuzz` input. Scheduled or hosted continuous fuzzing remains future work. diff --git a/book/src/development/release-security.md b/book/src/development/release-security.md new file mode 100644 index 00000000..742ab110 --- /dev/null +++ b/book/src/development/release-security.md @@ -0,0 +1,24 @@ +# Release security + +PhotoCraft's release workflow builds platform packages and a draft GitHub Release from the `release` branch. The authoritative process is [`docs/releasing.md`](https://github.com/storytold/photocraft/blob/main/docs/releasing.md). + +## Implemented workflow elements + +- platform-specific signing/notarization steps exist for macOS and Windows; +- signing secrets are scoped through the GitHub `release` environment; +- release artifacts include `SHA256SUMS.txt`; +- the final release job receives `contents: write`, while build jobs use narrower access; +- artifacts remain in a draft release until a maintainer publishes them. + +Signing is conditional. The workflow can produce unsigned artifacts when signing material is unavailable and emits warnings. Consumers and maintainers must verify the job summary and artifact signature rather than assuming every release is signed. + +## Future hardening + +- generate a machine-readable SBOM for each release; +- publish provenance/attestations tied to the source commit and workflow; +- make signature state explicit in release metadata; +- verify checksums, signatures, notarization, and installer behavior before publication; +- review action pinning and release-tool provenance; +- document key rotation and compromise response. + +Building, signing, drafting, publishing, and deploying are separate states. A successful build must not be reported as a published release. diff --git a/book/src/development/testing.md b/book/src/development/testing.md new file mode 100644 index 00000000..2a420e36 --- /dev/null +++ b/book/src/development/testing.md @@ -0,0 +1,27 @@ +# Testing + +Tests are the gate for source changes. Choose the narrowest relevant package tests during development, then run the repository gates appropriate to the touched layers. + +```sh +cargo test -p +cargo clippy -p --all-targets -- -D warnings +cargo xtask layers +``` + +For changes below or at L6, also run `cargo xtask wasm`. Command changes require the ignored adversarial test: + +```sh +cargo test -p photocraft-engine --test panic_hunt -- --ignored +``` + +Format changes should include valid round trips, exact boundary values, truncation, inconsistent lengths, malformed structures, and allocation/decompression limit cases. Pixel behavior should be exercised at applicable 8-bit, 16-bit, and 32-bit depths. + +UI changes need rendered evidence from the offscreen snapshot example or a control-channel screenshot. A successful build or HTTP response is not visual verification. + +Documentation changes are verified with: + +```sh +mdbook build book +``` + +Internal Markdown links and every `SUMMARY.md` destination should also be checked. The documentation workflow performs a clean mdBook build in CI. diff --git a/book/src/formats/overview.md b/book/src/formats/overview.md new file mode 100644 index 00000000..7c36352a --- /dev/null +++ b/book/src/formats/overview.md @@ -0,0 +1,13 @@ +# Format overview + +External bytes enter PhotoCraft through three main paths: + +- `photocraft-psd` parses and writes PSD and PSB structures. +- `photocraft-codecs` decodes and encodes flat raster formats. +- `photocraft-format` loads and saves the native `.pcraft` bundle. + +`photocraft-io` maps PSD and flat images into the document model and exports documents back to those formats. It is the integration layer; parsing limits remain the responsibility of the underlying format crates. + +Formats are a primary security boundary. File extension is not a trust signal, metadata can be attacker-controlled, and small compressed inputs can declare very large decoded outputs. Callers should use the bounded default APIs and preserve errors rather than retrying with unlimited limits. + +See [PSD and PSB](psd-psb.md), [Raster formats](raster-formats.md), and [The `.pcraft` format](pcraft.md). Cross-format hardening guidance is in [Parser hardening](../security/parser-hardening.md). diff --git a/book/src/formats/pcraft.md b/book/src/formats/pcraft.md new file mode 100644 index 00000000..3d0d99f5 --- /dev/null +++ b/book/src/formats/pcraft.md @@ -0,0 +1,29 @@ +# The `.pcraft` format + +`photocraft-format` stores the native lossless document as a ZIP archive or directory bundle. Its main entries are: + +```text +manifest.json +tiles/.zst +blobs/.zst +thumb.png +composite/preview.png +``` + +The manifest is versioned and describes the document tree. Tiles and blobs are content-addressed. Loaders verify ZIP entry bounds and CRCs; document content also uses stored hashes to detect mismatched or corrupted tile/blob content. Unsupported newer format versions fail explicitly. + +## Implemented load limits + +Default `LoadOptions` apply these maximums: + +| Limit | Default | +|---|---:| +| `manifest.json` | 256 MiB | +| One decompressed blob | 1 GiB | +| Total decompressed tiles and blobs | 16 GiB | + +The ZIP reader bounds decompression by the caller-supplied maximum, rejects encrypted entries and unsupported compression methods, validates offsets with checked arithmetic, and rejects CRC or declared-size mismatches. Corruption and property tests cover truncation, byte mutation, missing content, invalid manifests, excessive sizes, and random bytes. + +These defaults are intentionally finite but can still be expensive on constrained systems. Future hardening should add context-sensitive budgets and explicit limits for entry count, document complexity, and cumulative work, then test directory bundles against symlink and replacement races. + +When fields are added to `photocraft-doc`, `photocraft-format` intentionally requires corresponding manifest and conversion updates so native saves do not silently drop new state. diff --git a/book/src/formats/psd-psb.md b/book/src/formats/psd-psb.md new file mode 100644 index 00000000..d0f4e6b7 --- /dev/null +++ b/book/src/formats/psd-psb.md @@ -0,0 +1,23 @@ +# PSD and PSB + +`photocraft-psd` is a standalone clean-room reader and writer based on Adobe's public PSD specification. It supports PSD version 1 and PSB version 2, preserves unknown blocks for round-trip fidelity, and decodes channel data lazily. + +The parser accepts all four PSD compression methods handled by the crate and represents format-level structures before `photocraft-io` maps them to `photocraft-doc`. The current public API and fidelity behavior are documented in [`crates/psd/README.md`](https://github.com/storytold/photocraft/blob/main/crates/psd/README.md). + +## Implemented parser protections + +Current source enforces several explicit limits: + +- document width and height: at most 300,000 pixels each; +- document channels: at most 56; +- layer channels: at most 64; +- decoded channel data: at most 2 GiB for one decode layout; +- ActionDescriptor nesting: at most 64 levels; +- pattern edge: at most 30,000 pixels; +- arithmetic for row counts and decoded lengths uses checked operations. + +Malformed-input tests, property tests, and `cargo-fuzz` targets cover the main parser and ActionDescriptor parser. These controls reduce risk but do not prove the parser safe for every hostile file or bound total work across every embedded object. + +## Remaining hardening + +Priorities include broader PSB and embedded-object fuzz corpora, measuring aggregate allocations and parse time, ensuring every nested length is budgeted, and retaining minimized crashing inputs as regression fixtures. See [Fuzzing and regression testing](../security/fuzzing.md). diff --git a/book/src/formats/raster-formats.md b/book/src/formats/raster-formats.md new file mode 100644 index 00000000..693764d3 --- /dev/null +++ b/book/src/formats/raster-formats.md @@ -0,0 +1,22 @@ +# Raster formats + +`photocraft-codecs` provides format detection, capability declarations, decode limits, metadata handling, and encoding for flat images. The default build declares read/write support for PNG, JPEG, TIFF, WebP, GIF, BMP, TGA, ICO, Netpbm/PFM, QOI, OpenEXR, and Radiance HDR. AVIF encoding is optional; AVIF decoding is not implemented in the current crate. + +Capability details such as sample depth, channel layout, alpha, ICC, EXIF, XMP, DPI, and animation behavior are defined by `crates/codecs/src/format.rs`. Do not infer fidelity from the filename extension alone. + +## Implemented decode limits + +`DecodeOptions::default()` applies `Limits` before decoded pixel allocation: + +| Limit | Default | +|---|---:| +| Maximum width | 262,144 pixels | +| Maximum height | 262,144 pixels | +| Maximum pixel count | 268,435,456 pixels | +| Maximum decoded allocation | 2 GiB | + +Format adapters pass compatible allocation limits into PNG, TIFF, WebP, and `image`-based decoders, while other implementations perform explicit checked dimension/buffer validation. Tests cover bomb-like headers, truncation, bit flips, header overwrites, and random data. + +`Limits::none()` exists for callers with a deliberate reason to relax policy. It remains bounded by representable memory sizes, but it removes the default resource policy and should not be used for untrusted input. + +Animated containers are represented as a single decoded frame by the current codec API. Refer to [`crates/codecs/README.md`](https://github.com/storytold/photocraft/blob/main/crates/codecs/README.md) and source capability declarations for current behavior. diff --git a/book/src/getting-started/building.md b/book/src/getting-started/building.md new file mode 100644 index 00000000..83edc917 --- /dev/null +++ b/book/src/getting-started/building.md @@ -0,0 +1,32 @@ +# Building + +PhotoCraft uses the Rust workspace at the repository root. Install the stable Rust toolchain, clone the repository, and run commands from that root. + +```sh +cargo build --workspace +cargo run --release -p photocraft -- image.psd +cargo run -p photocraft-cli -- --help +``` + +The standard repository gates are: + +```sh +cargo fmt --all -- --check +cargo test --workspace +cargo clippy --workspace --all-targets -- -D warnings +cargo xtask layers +cargo xtask wasm +``` + +`cargo xtask wasm` requires the `wasm32-unknown-unknown` target. UI work should also be checked using the offscreen snapshot example described in the [development guide](https://github.com/storytold/photocraft/blob/main/docs/development.md). + +## Build the documentation + +Install mdBook, then build from the repository root: + +```sh +cargo install mdbook --locked +mdbook build book +``` + +The generated site is written to `book/output/` and is not source documentation. diff --git a/book/src/getting-started/contributing.md b/book/src/getting-started/contributing.md new file mode 100644 index 00000000..f1cbcaa5 --- /dev/null +++ b/book/src/getting-started/contributing.md @@ -0,0 +1,18 @@ +# Contributing + +Read [`AGENTS.md`](https://github.com/storytold/photocraft/blob/main/AGENTS.md) and the existing [contribution guide](https://github.com/storytold/photocraft/blob/main/docs/contributing.md) before editing source. Those files define the clean-room policy, crate layering, command checklist, testing requirements, naming, and visual verification rules. + +Important repository invariants include: + +- user-visible behavior is implemented as an engine command unless it is pure shell/view state; +- public pixel paths cannot assume 8-bit data or sRGB; +- command execution must return errors for adversarial input rather than panic; +- format work includes round-trip, malformed-input, and limit tests; +- L0-L6 changes must continue to compile for WebAssembly; +- third-party assets need a permissive license, a neighboring license file, and an `ATTRIBUTION.md` entry. + +## Security-sensitive contributions + +Before changing parsers, file I/O, serialization, automation, MCP, the control server, or command dispatch, read the [threat model](../security/threat-model.md) and [trust boundaries](../security/trust-boundaries.md). State whether a change adds an implemented control, documents a limitation, or proposes later hardening. Add a regression test for every fixed security defect when a safe test is possible. + +Do not include live exploit payloads, secrets, personal files, or unlicensed samples in issues, commits, fixtures, or fuzz corpora. Follow [Vulnerability reporting](../security/vulnerability-reporting.md) for undisclosed vulnerabilities. diff --git a/book/src/getting-started/installation.md b/book/src/getting-started/installation.md new file mode 100644 index 00000000..67509d2b --- /dev/null +++ b/book/src/getting-started/installation.md @@ -0,0 +1,15 @@ +# Installation + +PhotoCraft is currently an early-alpha project. Release artifacts, when available, are published through the repository's GitHub Releases page. Verify the release notes and checksums before running downloaded artifacts. + +For source builds, continue to [Building](building.md). The desktop application, headless CLI, and web target are separate workspace applications: + +- `apps/photocraft`: native desktop application +- `apps/photocraft-cli`: headless CLI, JSON-lines server, and MCP server +- `apps/photocraft-web`: WebAssembly application built with Trunk + +Platform packaging and signing details are maintained in the existing [release documentation](https://github.com/storytold/photocraft/blob/main/docs/releasing.md). Signing is conditional on the configured release secrets; the existence of an artifact alone is not proof that it was signed. + +## Trusting input files + +Treat PSD, PSB, `.pcraft`, raster images, profiles, LUTs, patterns, and embedded document content as potentially malicious. Review [Secure file handling](../security/secure-file-handling.md) before using unknown files in privileged or sensitive environments. diff --git a/book/src/project/credits.md b/book/src/project/credits.md new file mode 100644 index 00000000..8c1edf61 --- /dev/null +++ b/book/src/project/credits.md @@ -0,0 +1,7 @@ +# Credits + +PhotoCraft is created and maintained by the ArtCraft team and PhotoCraft contributors. Project ownership, copyright, and licensing remain as stated in the repository's `README.md`, `NOTICE`, and license files. Every merged contribution is credited in the repository history and release notes. + +## Documentation + +- [Gh0stlyKn1ght](https://github.com/Gh0stlyKn1ght): the initial documentation book and its security documentation (threat model, trust boundaries, parser hardening and fuzzing notes, vulnerability reporting guidance). diff --git a/book/src/project/licenses.md b/book/src/project/licenses.md new file mode 100644 index 00000000..8fd583e8 --- /dev/null +++ b/book/src/project/licenses.md @@ -0,0 +1,12 @@ +# Licenses + +PhotoCraft source is dual-licensed under MIT or Apache-2.0 at the user's option. The authoritative terms are the repository files: + +- [`LICENSE-MIT`](https://github.com/storytold/photocraft/blob/main/LICENSE-MIT) +- [`LICENSE-APACHE`](https://github.com/storytold/photocraft/blob/main/LICENSE-APACHE) +- [`NOTICE`](https://github.com/storytold/photocraft/blob/main/NOTICE) +- [`ATTRIBUTION.md`](https://github.com/storytold/photocraft/blob/main/ATTRIBUTION.md) + +Bundled third-party assets retain their own licenses and attribution requirements. The ArtCraft name, wordmark, and logos in `docs/brand/` are subject to [`docs/brand/LICENSE-brand.txt`](https://github.com/storytold/photocraft/blob/main/docs/brand/LICENSE-brand.txt) and are not covered by the source-code license. + +This page summarizes repository locations and does not alter any ownership or license term. diff --git a/book/src/project/roadmap.md b/book/src/project/roadmap.md new file mode 100644 index 00000000..7c5eea8a --- /dev/null +++ b/book/src/project/roadmap.md @@ -0,0 +1,15 @@ +# Roadmap + +The live product roadmap, milestone definitions, parity measurements, and current focus are maintained in [`docs/roadmap.md`](https://github.com/storytold/photocraft/blob/main/docs/roadmap.md). Menu coverage is generated into [`docs/parity.md`](https://github.com/storytold/photocraft/blob/main/docs/parity.md) by `cargo xtask parity`. + +Security hardening is cross-cutting rather than a replacement for those product milestones. The current security sequence is: + +1. threat model and reporting policy; +2. authenticated control sessions; +3. capability-scoped MCP/control access; +4. workspace-rooted filesystem handles; +5. request, batch, render, and connection budgets; +6. broader parser fuzzing and malicious-input regression corpora; +7. dependency policy and release SBOM/provenance. + +Only the documentation foundation in item 1 is established by this book. Later controls remain proposed until implemented and tested. diff --git a/book/src/security/automation-security.md b/book/src/security/automation-security.md new file mode 100644 index 00000000..df0e438d --- /dev/null +++ b/book/src/security/automation-security.md @@ -0,0 +1,59 @@ +# Automation security + +Automation is a privilege boundary because requests can cause filesystem access, application control, UI input, preferences changes, rendering, and command execution. + +## Current implementation + +- Desktop control binds to `127.0.0.1` and uses one JSON request/reply per line. +- Headless TCP refuses a successfully bound non-loopback address. +- MCP normally uses stdio and can optionally bridge to loopback control TCP. +- The bridge and UI request paths use timeouts. +- Engine commands are expected to reject invalid parameters without panicking. + +## Known limitations + +- no authentication or session establishment on control/headless TCP; +- no per-client or per-tool capabilities; +- no filesystem read/write root restriction; +- no bounded request line or JSON-depth policy at transport entry; +- no maximum batch length; +- no connection count or bounded worker pool; +- one thread per accepted TCP connection; +- no structured security audit event stream; +- full-size renders and expensive engine commands are not charged to a session budget. + +## Proposed session model + +A future security gateway should create a short-lived authenticated session from a cryptographically random per-launch credential. Authentication should occur before method discovery. + +Capabilities should be explicit and composable, for example: + +```text +DocumentRead +DocumentWrite +UiInspect +UiControl +FilesystemRead +FilesystemWrite +PreferencesWrite +ApplicationControl +``` + +Names and granularity remain a design proposal. The important property is that a client granted document inspection is not implicitly granted arbitrary file writes, pointer injection, preferences changes, or application shutdown. + +## Proposed request budgets + +The gateway should enforce and test finite values for: + +- request bytes and JSON nesting; +- batch steps and nested/recursive calls; +- concurrent connections and per-client in-flight requests; +- render dimensions and returned payload bytes; +- document pixels, decoded bytes, and aggregate session memory; +- command duration or cancellation where the engine supports it. + +Limits should fail with stable errors, be applied before expensive work, and be configurable only inside safe global ceilings. Audit events should capture session identity, capability decision, method/command ID, outcome, duration, and redacted path handle—not file contents, tokens, or secrets. + +## Deployment guidance today + +Prefer stdio, start control TCP only when needed, bind only to loopback, use a least-privileged OS account, and isolate untrusted agents from sensitive files. Do not tunnel or proxy the current loopback protocol to another host. diff --git a/book/src/security/fuzzing.md b/book/src/security/fuzzing.md new file mode 100644 index 00000000..af6b1a56 --- /dev/null +++ b/book/src/security/fuzzing.md @@ -0,0 +1,43 @@ +# Fuzzing and regression testing + +PhotoCraft already has `cargo-fuzz` workspaces next to its primary untrusted-format crates: + +| Crate | Targets | +|---|---| +| `crates/psd` | `parse`, `descriptor` | +| `crates/codecs` | `decode_any`, `decode_png`, `decode_jpeg`, `decode_tiff`, `decode_exr` | +| `crates/format` | `load` | + +The main CI workflow can run these targets through a manually dispatched fuzz job. It is not currently scheduled continuous fuzzing. + +Run a target from its crate directory with nightly Rust and `cargo-fuzz` installed: + +```sh +cd crates/psd +cargo fuzz run parse +``` + +The normal test suites complement libFuzzer with property tests, structured malformed cases, truncation sweeps, bit mutation, bomb-like headers, archive corruption, and command `panic_hunt` coverage. + +## Security properties + +A useful fuzz target should detect more than memory-safety crashes. Relevant failures include: + +- panic or abort; +- integer overflow or out-of-bounds access; +- unreasonable allocation or decompression expansion; +- timeout, infinite loop, or algorithmic complexity spike; +- recursion/stack exhaustion; +- malformed input accepted into an invalid internal state; +- inconsistent results between detection and explicit-format decode; +- corrupted `.pcraft` content accepted despite hash/CRC mismatch. + +## Corpus policy + +Every confirmed defect should produce a minimized, permanent regression test when disclosure and licensing allow it. Store only synthetic or redistributable inputs. Do not commit personal documents, customer data, credentials, proprietary assets, or an undisclosed weaponized sample. + +A regression entry should record the affected parser, expected error class, resource budget, and issue/advisory reference after disclosure. Fuzz crashes must be reproduced under a normal test before the fix is considered durable. + +## Future program + +Priority additions are PSB-specific structures, WebP, native directory bundles, ICC/LUT/pattern parsers, and embedded smart objects. Continuous scheduled fuzzing and artifact triage remain future work. diff --git a/book/src/security/overview.md b/book/src/security/overview.md new file mode 100644 index 00000000..a5a27a05 --- /dev/null +++ b/book/src/security/overview.md @@ -0,0 +1,28 @@ +# Security overview + +PhotoCraft processes complex attacker-controlled files and exposes automation that can act with the user's filesystem permissions. Security is therefore part of parser, engine, I/O, automation, release, and dependency design. + +## Current posture + +| Area | Implemented protections | Known limitations / future work | +|---|---|---| +| PSD/PSB | Header, channel, decoded-byte, nesting, and pattern limits; malformed/property tests; fuzz targets | Broader hostile corpus, aggregate work budgets, embedded-object coverage | +| Raster codecs | Default dimension/pixel/allocation limits; decoder-specific budgets; malformed/property tests; fuzz targets | Continuous fuzzing and more format-specific semantic cases | +| `.pcraft` | Manifest/blob/total-byte limits; bounded decompression; CRC/hash and version checks; fuzz target | Tighter complexity/entry limits and directory-bundle path-race analysis | +| Engine commands | Error-return contract, focused bad-parameter tests, adversarial `panic_hunt` test | Continue eliminating input-derived panic and exhaustion paths | +| Control TCP | Loopback binding and timeouts | No authentication, capabilities, request/batch/connection limits, or audit trail | +| MCP | Stdio transport; typed tool schemas; headless/bridge separation | No authorization scopes or filesystem policy | +| Dependencies | Cargo lockfile and normal CI build/test/clippy | No `cargo-audit` or `cargo-deny` job/configuration confirmed in current tree | +| Releases | Checksums and platform signing/notarization paths when secrets are configured | Signing can be absent; no SBOM generation confirmed in current tree | + +The table is a snapshot of the current repository, not a guarantee about deployed artifacts. Verify source and release evidence for high-risk use. + +## Security priorities + +1. Add authenticated, capability-scoped automation sessions. +2. Restrict automation file access through explicit workspace handles. +3. Bound request size, JSON complexity, batch length, connections, and render work. +4. Expand PSD/PSB, codec, and `.pcraft` fuzzing with regression corpora. +5. Add dependency advisories/license policy checks and release SBOMs. + +Start with the [Threat model](threat-model.md), then use the focused pages for file handling, parsers, fuzzing, and automation. diff --git a/book/src/security/parser-hardening.md b/book/src/security/parser-hardening.md new file mode 100644 index 00000000..3b653eff --- /dev/null +++ b/book/src/security/parser-hardening.md @@ -0,0 +1,38 @@ +# Parser hardening + +Parser safety is broader than “does not crash.” A parser must also bound memory, CPU time, recursion, output expansion, and the number of objects it creates. + +## Implemented controls + +### PSD/PSB + +`photocraft-psd` enforces maximum document dimensions and channel counts, bounds decoded channel data, limits ActionDescriptor nesting and pattern edges, and uses checked arithmetic for key decoded-size calculations. Tests cover malformed headers, truncation, property-generated inputs, and dedicated fuzz targets. + +### Raster codecs + +`photocraft-codecs` has configurable `Limits` for width, height, pixel count, and decoded allocation. The default policy is applied across enabled decoders and tested with oversized PNG, PNM, JPEG, and TIFF headers plus random/mutated inputs. + +### Native format + +`photocraft-format` bounds manifest, blob, and total decompressed bytes; validates ZIP structure and decompressed size; verifies CRCs and content hashes; rejects unsupported versions; and property-tests random bundles/manifests. + +## Review checklist + +For every attacker-controlled count, offset, dimension, or length: + +1. Parse into a type that can represent the file field without truncation. +2. Validate semantic limits before allocation. +3. Use `checked_add`, `checked_mul`, or a deliberately safe saturating comparison. +4. Verify a range lies inside the input before slicing. +5. Charge decoded bytes, objects, and recursion against a budget. +6. Return a format-specific error; never panic or loop indefinitely. +7. Test zero, maximum, maximum-plus-one, truncation, and inconsistent declarations. + +## Future hardening + +- define aggregate parse budgets shared across nested PSD resources and embedded objects; +- add time/work counters for highly fragmented but size-bounded files; +- exercise stack depth beyond ActionDescriptors, including recursive document relationships; +- add format-specific decompression ratio and object-count regression cases; +- benchmark worst-case accepted inputs on constrained machines; +- retain minimized fuzz artifacts with a non-sensitive provenance note. diff --git a/book/src/security/secure-file-handling.md b/book/src/security/secure-file-handling.md new file mode 100644 index 00000000..b3f6b4ed --- /dev/null +++ b/book/src/security/secure-file-handling.md @@ -0,0 +1,44 @@ +# Secure file handling + +PhotoCraft reads and writes documents in the desktop app, CLI, headless RPC server, and MCP server. Current automation accepts caller-supplied strings and converts them directly to `PathBuf`; render-to-file uses `std::fs::write`, and shared save code may create parent directories. + +## Implemented + +- Format loaders return structured errors for I/O and malformed data. +- `.pcraft` ZIP loading validates archive offsets, entry sizes, CRCs, content hashes, and configured decompressed-size budgets. +- Raster and PSD loaders apply parser/decode limits before major pixel allocation. +- Native `.pcraft` saves use temporary-file/rename patterns in relevant store paths. + +These are file-content protections, not path authorization. + +## Known limitations + +- Automation has no configured read or write root. +- Absolute paths and parent traversal are not rejected by a central policy. +- Canonicalization and symlink/junction handling are not enforced as an authorization boundary. +- Directory `.pcraft` bundles have not been documented as resistant to link replacement or time-of-check/time-of-use races. +- The same automation session can combine document access and general filesystem effects. + +## Proposed capability-based design + +Prefer an opened directory capability or workspace handle over repeated string sanitization: + +```text +AuthorizedWorkspace + - open_document(relative_path) + - save_document(relative_path) + - export_render(relative_path) +``` + +The handle should be created from an explicit user grant, preserve separate read/write permissions, reject absolute and escaping paths, define link behavior, and perform operations relative to the held directory capability. New files require a parent-directory handle because a non-existent target cannot be canonicalized safely. + +## Design requirements + +- Canonicalization alone is insufficient: links can change after validation. +- Policy should operate on handles and relative components where platform APIs allow it. +- Reads and writes should have distinct capabilities. +- Replace, create-new, truncate, and directory creation semantics must be explicit. +- Logs should avoid file contents and should minimize disclosure of full private paths. +- Tests should cover `..`, absolute paths, mixed separators, case behavior, symlinks/junctions, non-existent targets, and replacement races on supported platforms. + +Until this design is implemented, run untrusted automation inside an OS account, VM, container, or sandbox whose filesystem access already matches the intended workspace. diff --git a/book/src/security/threat-model.md b/book/src/security/threat-model.md new file mode 100644 index 00000000..e8a6bf58 --- /dev/null +++ b/book/src/security/threat-model.md @@ -0,0 +1,68 @@ +# Threat model + +This threat model covers the source repository's desktop, CLI, WebAssembly, format, and automation surfaces. It assumes files, paths, JSON, MCP messages, agent decisions, and embedded metadata may be malicious. + +## Assets + +- confidentiality of user documents, metadata, credentials, and unrelated local files; +- integrity of documents, preferences, filesystem contents, and application state; +- availability of PhotoCraft and the host system; +- integrity and provenance of release artifacts and dependencies; +- predictable command execution and document fidelity. + +## Threat actors + +- an author of a malicious image, PSD/PSB, `.pcraft`, profile, LUT, pattern, or embedded object; +- a malicious or compromised local process connecting to a loopback server; +- a malicious, compromised, or over-privileged MCP client or automation agent; +- a project/action file that induces unsafe paths or expensive commands; +- a compromised dependency or build/release environment. + +## Entry points + +```text +untrusted files and bytes + PSD / PSB / raster / .pcraft / ICC / LUT / pattern / embedded content + +untrusted control data + CLI arguments / filesystem paths / JSON-lines / MCP requests / agent commands + +untrusted build inputs + dependencies / CI actions / release secrets / packaging tools +``` + +## Primary threat scenarios + +### Hostile document parsing + +A compact file declares oversized dimensions, excessive channels, nested descriptors, corrupted offsets, or highly expanding compressed data. Desired outcome: reject it before unreasonable allocation or work, return an error, and retain a minimized regression test. + +### Filesystem authority abuse + +An automation client supplies an absolute path, traversal sequence, symlink, junction, or replaced path to read or overwrite data outside the intended project. Current automation uses caller-supplied OS paths and has no workspace policy. This scenario is a known limitation. + +### Local control takeover + +Another process on the same host connects to the loopback control server, discovers methods, drives UI input, executes commands, or opens/saves files. Loopback reduces network exposure but does not authenticate the client. + +### Resource exhaustion + +A client opens many TCP connections, sends an extremely long JSON line, creates a huge batch, requests full-size rendering, or repeatedly invokes expensive commands. Current automation transports lack explicit request, batch, and connection budgets. + +### Supply-chain compromise + +A vulnerable or malicious crate, action, installer tool, or signing environment changes build behavior or artifacts. Lockfiles and release checksums help reproducibility and verification, but advisory/license policy checks and SBOM output are future work. + +## Security objectives + +- reject malformed and oversized data before allocation where possible; +- use checked arithmetic and bounded recursion at parser/command boundaries; +- make automation authority explicit, narrow, and revocable; +- keep file access inside authorized roots despite traversal and link tricks; +- bound work per request and per session; +- preserve sufficient non-secret audit data to investigate automation actions; +- turn every fixed security bug into a permanent regression test. + +## Non-goals and limits + +PhotoCraft is not currently a sandbox for hostile automation or hostile native code. Loopback transport, Rust memory safety, typed schemas, and file-format checks do not replace authentication, authorization, OS isolation, or operational least privilege. This document does not claim those missing controls are present. diff --git a/book/src/security/trust-boundaries.md b/book/src/security/trust-boundaries.md new file mode 100644 index 00000000..1139aea1 --- /dev/null +++ b/book/src/security/trust-boundaries.md @@ -0,0 +1,47 @@ +# Trust boundaries + +The central boundary is crossed when untrusted bytes or requests become internal document state or authorized side effects. + +```text +UNTRUSTED + PSD / PSB + PNG / JPEG / TIFF / WebP / EXR and other raster files + .pcraft files and directory bundles + ICC profiles / LUT files / pattern files / embedded objects + CLI arguments / filesystem paths + JSON control messages / MCP requests / agent commands + | + v +SECURITY BOUNDARY + format parsers and decode budgets + automation transports and request validation + filesystem access layer + command registry, enabled checks, and parameter validation + serialization/deserialization + | + v +TRUSTED INTERNAL STATE + document model + engine Session and history + CPU/GPU composition plans + user-authorized filesystem effects +``` + +“Trusted internal state” means code may rely on validated invariants; it does not mean the data is secret-free or harmless to display. Metadata and text originating in a document remain tainted for logs, UI, filenames, and external commands. + +## Boundary ownership + +| Boundary | Primary code | Required checks | +|---|---|---| +| PSD/PSB bytes to model | `crates/psd`, then `crates/io` | lengths, dimensions, channels, nesting, decompression, offsets | +| Raster bytes to image | `crates/codecs` | dimensions, pixels, decoded allocation, format-specific limits | +| `.pcraft` to document | `crates/format` | archive bounds, decompressed totals, hashes, schema/version validation | +| JSON to command | `crates/automation`, `crates/engine` | request budget, method/capability, parameter type/range, enabled state | +| Path to filesystem effect | `crates/automation/src/files.rs`, app services | authorized root/handle, link policy, create/replace semantics | +| Document to GPU work | `crates/gpu`, UI planner | texture limits, buffer sizes, supported-plan validation | + +The filesystem and automation rows describe where policy belongs; the workspace-root, symlink, authentication, and capability controls are not yet implemented. + +## Agent guidance + +Agents must review this page before modifying any parser, file I/O, automation, MCP, control server, command execution, or serialization/deserialization path. A caller-provided path or tool request is data, not proof of user authorization. diff --git a/book/src/security/vulnerability-reporting.md b/book/src/security/vulnerability-reporting.md new file mode 100644 index 00000000..dd81cd6e --- /dev/null +++ b/book/src/security/vulnerability-reporting.md @@ -0,0 +1,9 @@ +# Vulnerability reporting + +The repository-level [`SECURITY.md`](https://github.com/storytold/photocraft/blob/main/SECURITY.md) is the authoritative reporting policy. + +Do not publish exploit details, weaponized files, secrets, or sensitive crash data before maintainers have had a reasonable opportunity to review and coordinate a fix. The repository currently documents no dedicated private security email or private reporting address. + +A useful report identifies affected versions or commits, platform/build configuration, impact, exact reproduction steps, expected versus actual behavior, and relevant logs or backtraces. For a malicious test file, first provide a minimal description, hash, format, and safe transfer requirements; do not attach it publicly when it could harm users or contains non-redistributable data. + +Security scope includes parsers, import/export, `.pcraft`, profiles/LUTs/patterns, command validation, automation, MCP, the control server, path handling, GPU resource validation, dependencies, CI, packaging, updates, and artifact verification.