This is an automated email from the ASF dual-hosted git repository.
sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-mcumgr.git
The following commit(s) were added to refs/heads/master by this push:
new 132840f * docs: add security threat model + discoverability files
132840f is described below
commit 132840fe3670de68421eb754cfe80526446d84a7
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Jul 28 09:32:17 2026 +0200
* docs: add security threat model + discoverability files
Adds a v0 THREAT_MODEL.md (for the PMC to review, own, and merge)
plus AGENTS.md and SECURITY.md providing the conventional
AGENTS.md -> SECURITY.md -> THREAT_MODEL.md discoverability chain.
All three files are new and carry the ASF license header.
Generated-by: Claude Opus 4.8 (1M context)
* Fold in Szymon Janc's PMC review answers
Szymon reviewed the draft on 2026-07-27 and answered twelve of the §14
questions inline. Two of those answers change the model's shape rather
than just ratifying a claim, so the body is rewritten accordingly.
Scope change — this model now covers the Mynewt porting layer only:
- Zephyr forked mcumgr into its own tree and no longer consumes this
repository, so Zephyr-only features and divergences are out of model.
Added as §3.5, and reflected in §1, §2 and the §2 family table. The
PMC's long-term plan is to move mcumgr back into apache/mynewt-core.
- Q14 (shell_mgmt as development-only) is consequently largely moot here:
Szymon notes it is a Zephyr-only feature.
Correction — there is no meaningful "default" command-group set on Mynewt:
- §5a asked which groups are default-on per OS. On Mynewt that is not a
well-formed question: what is compiled in follows from which packages
the app or a system component pulls in, per-sample. A group must be
explicitly enabled by the user, directly or transitively (enabling USB
may pull img_mgmt). Rewritten to say so.
Answers folded in as (maintainer):
- Q1 no authn/authz/confidentiality/integrity/replay at the SMP layer —
confirmed. Q2 MCUboot is the sole execution gate — correct. Q3
single-address-space, no privilege separation — confirmed.
- Q6 the upload chunk length is handed to cbor_read_object() via
cbor_attr_t, so the bound sits in the cborattr layer rather than an
explicit pre-copy check.
- Q8 the img_mgmt dummy-header / direct-upload toggles are unit-test
scaffolding, not a production surface.
- Q13 os_mgmt taskstat/mpstat are intentional disclosure. Q15 CBOR
nesting/size is not bounded before tinycbor — up to the decoder, which
makes that path a first-order review target. Q16 DoS/rate-limiting is
the transport's. Q17 no constant-time or side-channel guarantees.
- Q18 the AGENTS.md -> SECURITY.md -> THREAT_MODEL.md chain lands in this
same PR. Q19 scope to the Apache repo only.
Q7 (fs_mgmt path constraint) and Q9-Q12 (parse hardening) stay open and
stay marked (inferred). The status banner no longer claims the document
is unreviewed.
Generated-by: Claude Opus 5 (1M context)
---
AGENTS.md | 40 +++++
SECURITY.md | 43 +++++
THREAT_MODEL.md | 524 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 607 insertions(+)
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..d13f862
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,40 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Agent guidance
+
+This file is read by automated agents (security scanners, code analyzers,
+AI assistants) operating on this repository. It points them at the
+human-authored references they should consult before producing output.
+
+## Security
+
+Security model: [SECURITY.md](./SECURITY.md), which links to the project's
+threat model at [THREAT_MODEL.md](./THREAT_MODEL.md).
+
+Apache mcumgr is the device-side server for SMP (the Simple Management
+Protocol). Before reporting a security issue, consult `THREAT_MODEL.md`:
+SMP provides no authentication, authorization, or encryption of its own
+(those are the transport's and the integrator's responsibility), and an
+uploaded firmware image is gated by the bootloader's signature verification
+(MCUboot), not by mcumgr. The §11a "known non-findings" list enumerates the
+reports that are out of model by design; the in-model surface is memory
+safety in the frame-parse / dispatch / CBOR-decode path.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..dbfcce2
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,43 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Security Policy
+
+## Reporting a Vulnerability
+
+Please report suspected security vulnerabilities privately to the Apache
+Security Team at <[email protected]>, following the ASF process at
+<https://www.apache.org/security/>. Do not open public GitHub issues or
+pull requests for security reports.
+
+## Threat Model
+
+Apache mcumgr's security threat model — its scope and intended use, trust
+boundaries, the security properties it does and does not provide, the
+adversary model, and a list of recurring non-findings — is documented in
+[THREAT_MODEL.md](./THREAT_MODEL.md).
+
+Key point for triagers and scanners: SMP provides no authentication,
+authorization, confidentiality, or replay protection of its own. Those
+properties are delegated to the transport (e.g. BLE link encryption) and
+the integrator, and firmware-image authenticity is enforced by the
+signature-verifying bootloader (MCUboot), not by mcumgr. See
+`THREAT_MODEL.md` §9 and §11a.
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
new file mode 100644
index 0000000..aad5497
--- /dev/null
+++ b/THREAT_MODEL.md
@@ -0,0 +1,524 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Apache mcumgr Security Threat Model (draft)
+
+> **Status: v0, reviewed by the PMC.** This document was drafted by the
+> ASF Security team from public artefacts (the in-repo `README.md`,
+> `protocol.md`, `transport/*.md`, and the `cmd/*/syscfg.yml` defaults)
+> against the Scovetta threat-model rubric. **Szymon Janc (Mynewt PMC)
+> reviewed it on 2026-07-27** and answered the core-protocol, build-time,
+> scope and meta questions in §14; his answers are folded into the body.
+> Every non-trivial claim carries a provenance tag — *(documented)*
+> (stated in the repo's own files), *(maintainer)* (ratified or corrected
+> by a PMC member), or *(inferred)* (reasoned from code structure /
+> domain knowledge, and routed to a numbered question in §14). The
+> remaining *(inferred)* claims are the per-handler robustness questions;
+> corrections are still welcome on any of them.
+
+## §1 Header
+
+- **Project:** Apache mcumgr — a management library for 32-bit MCUs.
+- **Repository / commit:** `apache/mynewt-mcumgr`, `master` @ `0b63bc54308b`
(2026-05-20).
+- **Drafted:** 2026-06-13, by the ASF Security team (v0 draft from public
artefacts).
+- **Companion repos in the same review round:** `apache/mynewt-core`
+ (the RTOS mcumgr ports onto) and `apache/mynewt-nimble` (the BLE stack
+ that provides one of mcumgr's transports). **This model covers the
+ Mynewt porting layer only.** The codebase is OS-independent by
+ construction and the README still describes a Zephyr port, but Zephyr
+ **forked mcumgr into its own tree and no longer consumes this
+ repository**, so its divergences are out of model here. The PMC's
+ long-term plan is to move mcumgr back into `apache/mynewt-core`.
+ *(maintainer — §14 Q4, Q19)* Where a property is delegated to the host
+ OS or the bootloader, that is called out explicitly.
+- **What should trigger a revision of this model:** a new SMP command
+ group; a new transport; the addition of any authentication /
+ authorization layer to SMP; a change to which command groups are
+ compiled in by default; or a change to how uploaded images are gated
+ before execution. Internal refactors that do not change the wire
+ protocol, the command surface, or the trust boundary do **not** require
+ a revision.
+
+## §2 Scope and intended use
+
+mcumgr is **a library that a device-firmware author integrates into a
+product**, not a standalone application. It implements the device-side
+(**server**) half of **SMP — the Simple Management Protocol** — plus a
+set of management command handlers and pluggable transports. The remote
+(**client**) half is a separate tool (`mcumgr-cli` / `newtmgr`, in other
+repos) or a mobile/desktop app; it is **out of this model's scope** (and
+was explicitly declined from this review round by the PMC). *(documented:
+README; maintainer scope decision on the PMC's private list)*
+
+**Intended deployment.** mcumgr is compiled into the firmware image of an
+embedded device and exposed over one or more transports so that an
+operator (or a provisioning/field-update tool) can manage the device:
+upload new firmware, inspect state, read/write files, reset it. The
+canonical use is **device firmware update (DFU)** and field diagnostics.
+*(documented: README, `cmd/img_mgmt`)*
+
+### Component families (each modelled at its own trust/surface level)
+
+| Family | Directories | What it does | Surface |
+| --- | --- | --- | --- |
+| **SMP core** (frame + dispatch) | `smp/`, `mgmt/`, `omp/` | Parses the
`mgmt_hdr` frame header, dispatches by `(group, id)`, decodes the CBOR payload,
encodes the response. | The whole untrusted-input parse path — every byte after
the transport hands a frame up. |
+| **Command handlers** | `cmd/img_mgmt`, `cmd/fs_mgmt`, `cmd/os_mgmt`,
`cmd/log_mgmt`, `cmd/stat_mgmt`, `cmd/shell_mgmt` | Implement individual
commands within their group. Each has a **very different** privilege footprint
(firmware write vs. read a stat). | Per-command; see §6 / §11. |
+| **Transports** | `transport/` (BLE — `smp-bluetooth.md`; console/serial —
`smp-console.md`), plus UDP in some ports | Carry SMP frames between client and
server. | The reachability boundary — *who* can deliver a frame at all. |
+| **CBOR / attr decode** | `cborattr/`, bundled tinycbor | Decodes
attacker-controlled CBOR payloads into C structs. | Classic
untrusted-deserialization surface. |
+| **Ships-but-context-dependent** | `samples/`, `zephyr/` glue, `cmd/*/port/`
| Example apps and OS-porting shims. | Demo / integration code; modelled as the
integrator's, not the library's, per §3. The `zephyr/` glue is doubly out of
model — Zephyr forked mcumgr and no longer uses this repo (§3.5) *(maintainer)*
|
+
+### What mcumgr is *not*
+
+- It is **not** a secure channel. It provides **no** authentication,
+ authorization, confidentiality, integrity, or replay protection at the
+ SMP layer (see §9). *(maintainer — §14 Q1, confirmed)*
+- It is **not** the thing that decides whether an uploaded image is
+ allowed to *run*. mcumgr **stages** an image into a secondary slot; a
+ separate bootloader (**MCUboot**, in `apache/mynewt-core`'s `boot/`)
+ verifies the signature and decides whether to boot it. *(documented:
+ README "Dependencies"; §14 Q2)*
+- It is **not** OS-specific *by construction* — it relies on a porting
+ layer for flash, semaphores, and the like. But for the purposes of this
+ model the relevant porting layer is **Mynewt's**: Zephyr forked mcumgr
+ into its own tree and no longer uses this repository.
+ *(maintainer — §14 Q4)*
+
+## §3 Out of scope (explicit non-goals)
+
+1. **The SMP client** (`mcumgr-cli`, `newtmgr`, mobile apps). Separate
+ repos; declined from this round. A finding that requires a malicious
+ *client* is in-model only insofar as it is *the server* mishandling a
+ crafted frame (that is §6, and in scope) — the client's own code is
+ out.
+2. **Transport link security** — BLE pairing/bonding/encryption, serial
+ physical access control, UDP network reachability. That is the
+ transport's and the integrator's responsibility (§10), and for BLE
+ specifically it lives in `apache/mynewt-nimble`'s Security Manager,
+ not here.
+3. **Image authenticity / secure boot.** Whether an uploaded image is
+ signed, and whether an unsigned image can execute, is **MCUboot's**
+ job (`apache/mynewt-core` `boot/`). mcumgr's role ends at writing
+ bytes to the staging slot. *(documented: README; §14 Q2)*
+4. **The host RTOS and its porting layer.** Flash drivers, the scheduler,
+ the HAL, `os_` primitives — modelled in `apache/mynewt-core`. mcumgr
+ assumes they are correct (§5). A bug in the Mynewt porting layer is an
+ `apache/mynewt-core` finding, not an mcumgr one.
+ *(maintainer — §14 Q4)*
+5. **Zephyr and its fork.** Zephyr maintains its own fork of mcumgr in
+ the Zephyr tree and does not consume this repository. Zephyr-only
+ features and divergences are out of model here.
+ *(maintainer — §14 Q4, Q19)*
+6. **The integrating firmware author** — trusted by construction. mcumgr
+ has no concept of a "user"; it has a remote *peer* on a transport, and
+ that peer's trust level is entirely a property of the transport the
+ integrator chose to expose (§7).
+7. **Physical / invasive attacks** — chip decap, fault injection, bus
+ probing, glitching the boot. Out of model.
+8. **Supply-chain / build hygiene** — action pinning, release signing,
+ dependency currency. Not a threat-model concern.
+
+## §4 Trust boundaries and data flow
+
+There is exactly **one trust boundary that matters**: the point where a
+transport hands an SMP frame up to the SMP core. Everything the firmware
+already contains (handlers, the CBOR decoder, the flash routines) is in a
+**single trust domain** — one address space, no privilege separation, no
+MMU isolation between command groups *(inferred: embedded RTOS model —
+§14 Q3)*.
+
+```
+ UNTRUSTED | TRUSTED (firmware image)
+ |
+ attacker in radio range / on serial / on net |
+ | |
+ v raw SMP frame (mgmt_hdr + CBOR) |
+ [ transport RX ] --------------------------> | [ SMP core: parse mgmt_hdr ]
+ (BLE / console / UDP) | |
+ | v dispatch (group,id)
+ | [ command handler ]
+ | img/fs/os/log/stat/shell
+ | |
+ | v decode CBOR payload
+ | [ cborattr / tinycbor ]
+ | |
+ | v
+ | flash write (image slot) /
+ | file read-write / reset /
+ | stats read / shell exec
+```
+
+The trust transition is **frame ingress**. After that point there is no
+further internal boundary: a handler that the integrator compiled in can
+do whatever that command does, to whoever can send the frame. The
+security of the system therefore rests almost entirely on **who can put a
+frame on an enabled transport** (§7, §10) — not on anything SMP itself
+checks.
+
+## §5 Assumptions about the environment
+
+- **32-bit MCU, single physical address space, no process isolation, no
+ user/kernel split.** A memory-safety violation in any handler is a
+ whole-device compromise, not a contained one. *(inferred — §14 Q3)*
+- **The host OS porting layer is correct** — flash read/write/erase,
+ semaphores, the SMP transport driver. mcumgr's guarantees are
+ conditional on these. *(documented: README "relies on hardware porting
+ layers"; §14 Q4)*
+- **For image execution to be gated, MCUboot (or an equivalent
+ signature-verifying bootloader) is present and configured to reject
+ unsigned/untrusted images.** Without that, "upload an image" becomes
+ "run arbitrary code" — but that is a deployment property, not an
+ mcumgr property. *(documented: README; §14 Q2)*
+- **The firmware author chose which transports to expose and which
+ command groups to compile in.** mcumgr does not pick these. *(documented:
+ per-package `syscfg.yml`)*
+
+## §5a Build-time and configuration variants
+
+mcumgr's attack surface is **defined at build time** by which command
+groups are linked in and how they are configured. This section is
+load-bearing: the same library is benign or dangerous depending on these
+choices.
+
+- **Which command groups are compiled in.** `img_mgmt`, `fs_mgmt`,
+ `os_mgmt`, `log_mgmt`, `stat_mgmt`, `shell_mgmt` are separate packages.
+ `shell_mgmt` (remote shell command execution) and `fs_mgmt` (arbitrary
+ file read/write) are the highest-power groups and should be off in
+ most production builds.
+ **On Mynewt there is no meaningful "default" set.** What is compiled in
+ follows from which packages the application — or a system component —
+ pulls in, which is a per-sample configuration choice. The rule of thumb
+ is that a group must be **explicitly enabled by the user**, either
+ directly in the app or transitively by pulling a package that depends
+ on it (enabling USB, for instance, may pull `img_mgmt`). So "is it on by
+ default?" is the wrong question to ask of this repo; "which packages
+ does this product's build pull in?" is the right one.
+ *(maintainer — §14 Q5)*
+- **Upload chunk buffers are stack-allocated.**
+ `IMG_MGMT_UL_CHUNK_SIZE` and `FS_MGMT_UL_CHUNK_SIZE` default to **512**
+ and the syscfg descriptions state "a buffer of this size gets
+ allocated **on the stack** during handling of an upload." The handler
+ must therefore validate the attacker-supplied chunk length against this
+ bound *before* copying — a classic embedded stack-overflow sink if the
+ check is wrong or missing. *(documented: `cmd/img_mgmt/syscfg.yml`,
+ `cmd/fs_mgmt/syscfg.yml`; §14 Q6)*
+- **`FS_MGMT_PATH_SIZE` (default 64)** bounds the file-path buffer for
+ `fs_mgmt`. Path handling against this bound, and any path-traversal
+ containment, is a per-build concern. *(documented: `cmd/fs_mgmt/syscfg.yml`;
+ §14 Q7)*
+- **Frame endianness.** SMP adds optional little-endian support on top of
+ NMP's mandatory big-endian header. Both decode paths are reachable.
+ *(documented: `protocol.md`)*
+- **`img_mgmt` "dummy header" / direct-image-upload toggles** appear in
+ syscfg and change behaviour; their security relevance needs maintainer
+ input. *(documented: `cmd/img_mgmt/syscfg.yml`; §14 Q8)*
+
+## §6 Assumptions about inputs
+
+The **only** untrusted input is the **SMP frame** delivered by a
+transport. If the attacker can reach an enabled transport, **every byte
+of the frame is attacker-controlled** — header and payload alike.
+
+### SMP frame header trust table (`struct mgmt_hdr`)
+
+| Field | Meaning | Attacker-controllable? | Handler/core must enforce |
+| --- | --- | --- | --- |
+| `nh_op` (3 bits) | READ / WRITE / *_RSP | **yes** | reject/ignore response
opcodes arriving at a server; route only valid ops *(inferred — §14 Q9)* |
+| `nh_flags` | reserved | **yes** | defined-bit validation; ignore reserved
bits safely *(documented: "TBD"; §14 Q9)* |
+| `nh_len` | **claimed** payload length | **yes** | the claimed length must be
validated against the *actually received* byte count and against buffer bounds
before use — never trusted as the copy size *(inferred — §14 Q10)* |
+| `nh_group` | command group selector | **yes** | dispatch only to a
registered group; unknown group → clean error, not UB *(inferred — §14 Q11)* |
+| `nh_seq` | sequence number | **yes** | no replay/ordering guarantee is
claimed (§9); used only to correlate response *(documented: "TBD"; §14 Q12)* |
+| `nh_id` | command within group | **yes** | dispatch only to a registered
handler; unknown id → clean error *(inferred — §14 Q11)* |
+
+### CBOR payload trust table (per high-value command)
+
+| Command group → command | Attacker-supplied fields | What the handler does
with them | Must enforce |
+| --- | --- | --- | --- |
+| `img_mgmt` upload | image `data` chunk, `off`(set), `len`, `sha` | copies
`data` into a **stack** buffer, writes it to the staging flash slot at `off` |
`len`/chunk-size validated vs `IMG_MGMT_UL_CHUNK_SIZE` *before* copy; `off`
monotonic / bounded to slot size; **never** treat acceptance as "this image is
trusted" (that is MCUboot, §3.3) *(documented + inferred — §14 Q6)* |
+| `fs_mgmt` upload/download | file `name`/path, `off`, `data`, `len` | opens a
file at `name`, reads/writes at `off` | path bounded to `FS_MGMT_PATH_SIZE`;
**path-traversal containment** to an intended directory (or the integrator
accepts full-FS access); chunk bound as above *(inferred — §14 Q7)* |
+| `os_mgmt` taskstat / mpstat / datetime / reset | command params | returns
task/memory diagnostics; performs a device reset | bounded output encoding;
reset only after the documented delay *(documented: reset delay syscfg)* — note
these **leak internal state** and **reboot the device** to anyone on the
transport, by design *(inferred — §14 Q13)* |
+| `shell_mgmt` exec | a shell command line | executes it in the device shell |
**this is arbitrary command execution by design** when the group is compiled
in; intended for trusted/dev contexts only *(inferred — §14 Q5/Q14)* |
+
+### Size / shape / rate
+
+- **CBOR depth/size.** The payload is CBOR decoded by the bundled
+ tinycbor/`cborattr`. Robustness against deeply-nested, truncated, or
+ oversized CBOR is the decoder's job; whether mcumgr caps nesting/size
+ before decode is unknown to us. *(inferred — §14 Q15)*
+- **No SMP-layer rate limiting.** mcumgr does not rate-limit inbound
+ frames; backpressure is the transport's. *(inferred — §14 Q16)*
+
+## §7 Adversary model
+
+### Actors
+
+| Actor | In scope? | Capabilities |
+| --- | --- | --- |
+| **Anyone who can deliver an SMP frame over an *enabled* transport** | **yes
— the primary adversary** | craft arbitrary SMP frames (any group/id/len, any
CBOR), replay, flood, fuzz. For BLE: in radio range (bounded by whatever link
security the integrator enabled in nimble — possibly none). For serial:
physical/console access. For UDP: network reachability. |
+| **A malformed-but-deliverable frame from an otherwise "legitimate" client**
(buggy or compromised management tool) | **yes — parser robustness must hold**
| drives the server's parse/dispatch/decode path with adversarial bytes;
in-model for memory safety, hang, unbounded stack/heap use. |
+| **The transport's link-layer peer** (e.g. a paired BLE central) |
**conditionally** | if the integrator required BLE bonding+encryption, the
adversary is reduced to a *bonded* peer; if not, it is anyone in range. The
*reduction* is the integrator's doing, not mcumgr's (§10). |
+| **The integrating firmware author** | **out of scope** | trusted; chooses
transports, enabled groups, and the bootloader policy. |
+| **Physical-invasive attacker** (glitch, decap, bus probe) | **out of scope**
| §3.6. |
+| **Side-channel observer** | **out of scope** *(inferred — §14 Q17)* | no
constant-time or side-channel-resistance claim is made by mcumgr. |
+
+### The defining asymmetry
+
+Because SMP performs **no authentication or authorization** (§9), the
+adversary's power is **entirely determined by transport reachability and
+the set of enabled command groups** — not by anything mcumgr checks. A
+device that exposes `img_mgmt` + `fs_mgmt` + `shell_mgmt` over an
+unencrypted, un-bonded BLE connection is, by design, **fully controllable
+by anyone in radio range**. This is not a vulnerability in mcumgr; it is
+the integrator operating mcumgr outside its intended trust assumptions
+(§10, §11). Conversely, a memory-safety bug in the frame parser **is**
+mcumgr's, because it breaks even when the transport is perfectly secured.
+
+## §8 Security properties the project provides
+
+For each: condition, violation symptom, severity, provenance. The list is
+deliberately short — mcumgr is a thin protocol server, and most security
+properties of a deployed device are delegated to the transport, the
+bootloader, and the integrator (§10).
+
+1. **Memory-safe parsing and dispatch of SMP frames on the compiled-in
+ command groups, given a correct porting layer.** *(inferred — §14 Q10/Q11;
this is the single most important property to confirm or qualify)*
+ - *Violation symptom:* a crafted frame (bad `nh_len`, oversized upload
+ chunk, malformed CBOR, unknown group/id) causes out-of-bounds
+ read/write, stack overflow, or controlled corruption.
+ - *Severity:* **high** — single address space, so corruption is
+ whole-device (§5).
+2. **Bounded stack use during uploads, when chunk length is validated
+ against `*_UL_CHUNK_SIZE`.** *(documented config + inferred enforcement —
§14 Q6)*
+ - *Violation symptom:* an upload chunk larger than the configured
+ bound overruns the stack buffer.
+ - *Severity:* **high**.
+3. **Faithful implementation of the SMP wire format** (header layout,
+ op/group/id semantics, CBOR encoding of responses). *(documented:
protocol.md)*
+ - *Violation symptom:* a response that misencodes lengths/IDs such
+ that a conforming client mis-parses it.
+ - *Severity:* **low–medium** (interop / client-side, mostly).
+4. **Clean rejection of unknown groups/commands** (no dispatch into
+ unregistered handlers). *(inferred — §14 Q11)*
+ - *Violation symptom:* an unknown `(group,id)` reaches uninitialised
+ function state.
+ - *Severity:* **medium–high**.
+
+mcumgr explicitly does **not** appear to claim graceful behaviour under
+*resource exhaustion* (flooding) or any property about *malicious but
+well-formed* commands (those are §9/§10, by design).
+
+## §9 Security properties the project does *not* provide
+
+This is the most important section for triage, because mcumgr **looks
+like** a management/security feature and is routinely assumed to provide
+properties it does not.
+
+- **No authentication.** SMP carries no credential. The server does not
+ verify *who* sent a frame. *(inferred — §14 Q1)*
+- **No authorization.** There is no per-command permission model; if a
+ group is compiled in, every command in it is available to every peer
+ that can reach the transport. *(inferred — §14 Q1)*
+- **No confidentiality.** SMP frames are plaintext; any payload secrecy
+ is the transport's (e.g. BLE link encryption). *(inferred — §14 Q1)*
+- **No integrity / no anti-tamper / no replay protection at the SMP
+ layer.** `nh_seq` correlates responses; it is not a nonce. *(documented:
+ "Seq: TBD"; §14 Q12)*
+- **No guarantee that an accepted firmware image is authentic or
+ bootable.** mcumgr writes bytes to a slot; **MCUboot** decides whether
+ the image runs. An "unauthenticated firmware upload" finding against
+ mcumgr is OUT-OF-MODEL — it is intended behaviour with the gate
+ elsewhere (§3.3, §11a). *(documented: README; §14 Q2)*
+- **No DoS resistance.** A peer that can reach the transport can flood,
+ reset (`os_mgmt`), or wedge the device; mcumgr makes no availability
+ guarantee against an on-transport adversary. *(inferred — §14 Q16)*
+- **No constant-time / side-channel guarantees.** *(inferred — §14 Q17)*
+
+### False friends
+
+- **"SMP" / "management protocol" sounds authenticated.** It is not — it
+ is a transport-agnostic RPC with no security layer of its own.
+- **`img_mgmt` "image hash" (`sha`) is an integrity/identification aid,
+ not an authenticity check.** The cryptographic *authenticity* gate is
+ MCUboot's signature verification, separately. *(inferred — §14 Q2)*
+
+## §10 Downstream responsibilities
+
+The integrator — not mcumgr — owns device security. Concretely:
+
+1. **Gate the transport.** Require BLE bonding + LE Secure Connections
+ encryption (configured in nimble); restrict serial to
+ physically-trusted access; firewall/authenticate any UDP transport.
+ An open transport = full device control to anyone who can reach it.
+2. **Compile in only the groups you need.** Disable `shell_mgmt` and
+ `fs_mgmt` in production unless the product genuinely requires remote
+ shell / file access. Treat them as debug-only.
+3. **Enable secure boot.** Ensure MCUboot (or equivalent) is present and
+ configured to **reject unsigned/untrusted images**, so `img_mgmt`
+ upload cannot become code execution.
+4. **Bound and rate-limit at the transport** if availability matters.
+5. **Assume any reachable, enabled command is fully exercised by an
+ adversary.** Threat-model the *product* on that basis.
+
+## §11 Known misuse patterns
+
+- Exposing `img_mgmt`/`fs_mgmt`/`shell_mgmt` over an **unencrypted,
+ un-bonded BLE** connection in a shipped product.
+- Shipping with **`shell_mgmt` enabled** (remote arbitrary command
+ execution) because it was handy in development.
+- **Relying on mcumgr for access control** ("only our app speaks SMP") —
+ any peer on the transport can speak SMP.
+- **Assuming firmware upload needs a credential** — it does not; only
+ MCUboot's signature check stands between an uploaded image and boot,
+ and only if enabled.
+- **Disabling MCUboot signature verification** for convenience while
+ leaving `img_mgmt` reachable.
+- Treating the `sha`/hash in image upload as an authenticity guarantee.
+
+## §11a Known non-findings (recurring false positives)
+
+These are the reports a scanner/fuzzer/researcher will most predictably
+file against mcumgr that are **out of model by design**. Triagers should
+close them with the cited section.
+
+| Reported as | Why it is a non-finding | Cite |
+| --- | --- | --- |
+| "SMP endpoint has no authentication / authorization" | By design —
authn/authz is not an mcumgr property; the integrator gates the transport. |
§9, §10.1 |
+| "Unauthenticated firmware update / DFU over BLE/serial" | Intended; image
*execution* is gated by MCUboot signature verification, which is separate. |
§3.3, §9 |
+| "`shell_mgmt` allows arbitrary command execution" | Opt-in, build-time-gated
debug feature for trusted contexts; not compiled in for hardened builds. | §5a,
§6, §10.2 |
+| "`os_mgmt` taskstat/mpstat leaks internal memory/task layout" | Diagnostic
by design; same transport-trust assumption as every other command. | §6, §9 |
+| "`os_mgmt` reset lets a peer reboot the device (DoS)" | No availability
guarantee against an on-transport adversary is claimed. | §9 |
+| "No replay protection — frames can be replayed" | Correct; `nh_seq` is a
correlator, not a nonce; not claimed. | §9 |
+| "SMP frames are sent in cleartext" | Confidentiality is the transport's job
(BLE link encryption), not SMP's. | §9, §10.1 |
+| "Image `sha` is not a real signature" | Correct — it is an
identifier/integrity aid; authenticity is MCUboot's. | §9 false-friends |
+
+Anything resting on **"…but there's no authentication/encryption"** is
+almost certainly this row, not a bug — confirm the report instead
+describes a *memory-safety* or *bounds* failure in the parse/dispatch/
+decode path before treating it as valid (§13).
+
+## §12 Conditions that would change this model
+
+- Adding an authentication or authorization layer to SMP (would create
+ real §8 properties and move several §9 items).
+- A new transport whose default reachability differs (e.g. an always-on
+ IP transport).
+- A new command group, or making a currently-opt-in group (shell, fs)
+ default-on.
+- Moving image-signature verification *into* mcumgr (today it is
+ MCUboot's).
+- A change to where upload buffers live (stack → heap) or how chunk
+ bounds are enforced.
+
+## §13 Triage dispositions
+
+| Disposition | Use when |
+| --- | --- |
+| **VALID** | Memory-unsafety, stack/heap overflow, OOB read/write, or
unbounded resource use reachable by a crafted SMP frame on a compiled-in
handler (§6, §8) — i.e. it breaks *even with the transport perfectly secured*. |
+| **OUT-OF-MODEL** | The report depends on the absence of
authn/authz/encryption/replay protection, or on a malicious-but-well-formed
command (§9). These are intended; the gate is the transport / bootloader /
build config. |
+| **DOWNSTREAM** | The fix is the integrator's: enable bonding, disable a
group, turn on MCUboot signature verification, firewall the transport (§10). |
+| **NON-FINDING** | Matches a §11a row. |
+| **MODEL-GAP** | Real and in spirit in-scope, but no §8 property or §9
disclaimer covers it yet → feeds a §14 question and a model update. |
+
+## §14 Open questions for the maintainers
+
+Grouped; each promotes an *(inferred)* claim above to *(maintainer)* once
+answered. **Szymon Janc (Mynewt PMC) answered the following on
+2026-07-27**; his answers are folded into the body above and retained
+here.
+
+**Core protocol / trust — ANSWERED**
+- **Q1.** SMP provides no authentication, authorization, confidentiality,
+ integrity or replay protection of its own; all delegated to the
+ transport/integrator. → **Confirmed.**
+- **Q2.** mcumgr stages an image; MCUboot's signature verification is the
+ sole gate on execution. → **Correct.**
+- **Q3.** Single-address-space / no privilege separation — a memory bug in
+ any handler is whole-device. → **Confirmed.**
+
+**Porting / environment — ANSWERED**
+- **Q4.** Which porting-layer guarantees does mcumgr rely on?
+ → **Scope this model to the Mynewt porting layer.** Zephyr forked
+ mcumgr into its own tree and no longer uses this repository. The PMC's
+ long-term plan is to move mcumgr back into `apache/mynewt-core`.
+ (§1, §2, §3.4, §3.5)
+
+**Build-time surface**
+- **Q5.** Which command groups are default-on? → **On Mynewt there is no
+ clear "default".** What is compiled in depends on which packages the
+ application or a system component pulls in, and that is a per-sample
+ configuration choice. The rule of thumb is that a group must be
+ **explicitly enabled by the user** — either directly in the app, or
+ transitively by pulling a package that depends on it (e.g. enabling USB
+ may pull `img_mgmt`). Zephyr's defaults are out of model (see Q4).
+ *(maintainer)*
+- **Q6.** Where is the attacker-supplied chunk length validated relative
+ to the stack copy? → **Handed to `cbor_read_object()` via `cbor_attr_t`**
+ — i.e. the bound is enforced by the cborattr layer rather than by an
+ explicit pre-copy check in the handler. *(maintainer)* The robustness of
+ that path is still worth a scan's attention (§6).
+- **Q7.** Does `fs_mgmt` constrain paths to an intended directory, or is
+ full-filesystem read/write the intended (integrator-gated) behaviour?
+ *(still open)*
+- **Q8.** What do the `img_mgmt` "dummy header" / direct-upload syscfg
+ toggles do? → **They are used in unit tests.** Not a production
+ security surface. *(maintainer)*
+
+**Parse hardening**
+- **Q9.** How are reserved `nh_flags` bits and server-side receipt of
+ `*_RSP` opcodes handled?
+- **Q10.** Is `nh_len` ever used as a copy size before being validated
+ against the actually-received byte count?
+- **Q11.** Behaviour on unknown `(group,id)` — guaranteed clean error?
+- **Q12.** Confirm `nh_seq` carries no security/ordering guarantee.
+- **Q13.** Are `os_mgmt` diagnostics (taskstat/mpstat) intentional
+ information disclosure to any transport peer? → **Yes.** *(maintainer)*
+- **Q14.** Is `shell_mgmt` intended strictly for development? → **Believed
+ so — but it is a Zephyr-only feature**, so for this model (Mynewt
+ porting layer, Q4) it is largely moot. *(maintainer)*
+- **Q15.** Does mcumgr bound CBOR nesting/size before handing the payload
+ to tinycbor? → **No — that is up to the decoder.** Robustness against
+ hostile CBOR is tinycbor's/cborattr's, which makes that path a
+ first-order target for review (§6). *(maintainer)*
+- **Q16.** Any intended DoS/rate-limit posture? → **It is on the
+ transport.** mcumgr claims none of its own. *(maintainer)*
+- **Q17.** Confirm no constant-time / side-channel guarantees are claimed.
+ → **Confirmed.** *(maintainer)*
+
+**Meta — ANSWERED**
+- **Q18.** OK for this `THREAT_MODEL.md` to become the canonical model,
+ reached via `AGENTS.md → SECURITY.md → THREAT_MODEL.md`? → **Yes** —
+ that chain lands in this same PR.
+- **Q19.** Should the model scope itself to the Apache repo only, or note
+ Zephyr divergences? → **Apache repo only.** Zephyr forked mcumgr and no
+ longer consumes this repository (§3.5). *(maintainer)*
+
+**Still open** — Q7 (`fs_mgmt` path constraint) and the parse-hardening
+questions Q9–Q12 above. Szymon noted some of these touch very low-level
+detail; they are not blocking and the affected claims stay *(inferred)*.
+
+## Appendix: existing security-policy artefacts → §x back-map
+
+At `master @ 0b63bc54308b`, `apache/mynewt-mcumgr` contains **no
+`SECURITY.md`, no `AGENTS.md`, and no prior threat-model document**
+(only `protocol.md` and the per-transport `transport/*.md` notes, which
+are protocol documentation, not security policy). There is therefore
+nothing to back-map or supersede; this is a greenfield v0. The
+`protocol.md` frame/format details are cited inline above as
+*(documented)*.