feat(vti-common)!: make Capability and AuditEvent non-exhaustive - #1262
Conversation
Both enums are designed to grow — `Capability` gains an entry whenever the agent gains a power worth gating separately, and `AuditEvent`'s own doc comment says variants arrive alongside the features that emit them. Neither was `#[non_exhaustive]`, so every one of those additions was a breaking change for anyone matching on them. That is not hypothetical. `MemoryRead`, `MemoryWrite`, `RoomPresent`, `RoomOpen` and `AuditEvent::RoomOperation` went out in vti-common 0.16.2 — a PATCH release — so a downstream exhaustive `match` stopped compiling on a routine `cargo update`, with the caret requirement picking it up automatically. The cost inside this workspace is zero: nothing here matches exhaustively on either type. Every reference constructs a variant as a value, and the only `match self` sits in `vti-common` itself, where the attribute has no effect. Checked before writing it, not after. Downstream code now needs a `_ =>` arm, and that is the point rather than the price. A capability a consumer has never heard of is precisely the one it must not silently treat as granted, and an audit event it cannot name still has to be recorded; a wildcard arm forces both decisions to be written down instead of being decided by a compile error at the wrong moment. Deliberately NOT applied to the sixteen `vta-sdk` wire structs that broke the same way when they gained `ext`. `#[non_exhaustive]` on a struct removes literal construction from outside the crate entirely — functional update with `..Default::default()` included, which is the part people assume still works — so all sixteen would need constructors or builders. That is a redesign of the public SDK surface, not cleanup, and the safety half is now covered anyway: a new field forces a breaking bump and #1256's guard makes that stick. Worth doing deliberately, in its own change. Breaking for external consumers, so this needs the minor slot (0.17.0) rather than a patch. The guard added in #1256 will now say so if the release proposes otherwise — which makes this its first live exercise of the failure path. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
🛡️ AI Agentic Security Code Review2 findings need a human to review/validate. Mandatory to check: 🔒 Security Code Review Report Details🛡️ Security Code Review Report — PR #1262
🗺️ Scan CoverageModules scanned: 1 · with findings: 1 · files: 2 · findings: 4
Executive Summary
🔒 Security Issues
|
| Field | Detail |
|---|---|
| Severity | MEDIUM |
| Location | vti-common/src/acl/mod.rs:118 |
| Finding ID | github_pr-f1c409ab595b |
| CWE | CWE-284, CWE-863, CWE-440 |
| OWASP | A01:2021 - Broken Access Control |
| MITRE ATT&CK | T1548 - Abuse Elevation Control Mechanism |
| CAPEC | CAPEC-122, CAPEC-180 |
| DREAD | 6.4 |
| Reachability | 🔴 Reachable |
| Exploit Maturity | conceptual |
| Detection Source | skill_scan |
🧠 AI Triage:
- Severity reassessed: HIGH → MEDIUM — CVSS is not available, so severity is assessed on CWE class + reachability + business impact. The design flaw is confirmed present in the code (non_exhaustive enum, no built-in deny-by-default helper) and is reachable via the library's public API by any downstream consumer, in a production-designated repository core to a 'trust infrastructure' product (vault/memory/room access control). However, exploitability is only medium/conceptual — there is no PoC, no known exploited downstream consumer with a permissive wildcard shown in the evidence, and impact requires a future capability addition plus an existing permissive match arm. This keeps it at high rather than critical: high impact and plausible attack path, but missing confirmed exploit evidence and a directly demonstrated vulnerable consumer.
- Composite score: 5.5
- Environment: production
Summary: vti-common's Capability enum is intentionally #[non_exhaustive] so new variants can ship in patch releases; this forces downstream wildcard match arms, and if any such arm defaults to allow rather than deny, new capabilities are silently granted without any code change or attacker action needed beyond triggering the gated operation.
📝 Description:
In any downstream service using vti-common for ACL enforcement, a badly-written wildcard arm results in unauthorized principals gaining vault-read/write, memory-read/write, or room-present/open capabilities the moment vti-common ships a routine patch release — with no attacker-visible signal and no downstream code change required.
🧪 Proof of Concept:
The #[non_exhaustive] attribute permits new enum variants to be added without a semver-major bump. The enum itself contains no vulnerability, but it structurally guarantees that every downstream consumer must write a fallback match arm — and the security outcome then depends entirely on whether that arm is written deny-safe or allow-unsafe, a decision vti-common cannot enforce at compile time.
/// produces a sensible default from the existing role (Admin gets
/// everything, Reader gets only `vault-read`, etc.) so existing ACL
/// behaviour is preserved bit-for-bit.
/// **Non-exhaustive on purpose.** ...
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Capability {
VaultRead,
VaultWrite,
Vulnerable lines: 118, 124
🔁 Reproduction Steps:
- Clone a hypothetical downstream crate depending on vti-common = "0.16" that implements: match cap { Capability::VaultRead => check_read(), Capability::VaultWrite => check_write(), _ => true }
- Run
cargo update -p vti-commonto pick up a patch release introducing Capability::RoomOpen (per documented 0.16.2 precedent). - Call the ACL check function with Capability::RoomOpen for a principal who has never been granted it.
- Observe the function returns true/Grant via the wildcard arm, without recompiling or reviewing the downstream match.
🔎 Evidence: vti-common/src/acl/mod.rs:118
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Capability {
VaultRead,
VaultWrite,
💥 Impact:
In any downstream service using vti-common for ACL enforcement, a badly-written wildcard arm results in unauthorized principals gaining vault-read/write, memory-read/write, or room-present/open capabilities the moment vti-common ships a routine patch release — with no attacker-visible signal and no downstream code change required.
Confidentiality: High — unauthorized read access to vault/memory data possible if a new *-Read capability is silently granted · Integrity: High — unauthorized write access to vault/memory or room state possible if a new -Write/-Open capability is silently granted · Availability: None directly
🧭 Reachability:
- Network exposure: internal
- Auth barrier: basic
- Attack path: EP-001 (Capability enum LIBRARY_API) → downstream crate's ACL match statement → wildcard arm → grant/deny decision on gated resource (vault/memory/room)
⚖️ Triage Factors:
| Factor | Value |
|---|---|
| Fixable | ✅ Yes |
| Exploitability | medium |
| Business impact | high |
| Public exploit | None known |
| Environment | unknown |
Attack scenario: A downstream consumer with a permissive wildcard ACL match silently grants any newly-introduced Capability variant shipped in a vti-common patch release, before the consumer's own code is updated to explicitly handle it.
🔧 Remediation:
⚠️ AI-generated fix. Review, test in staging, and validate against your architecture before applying.
Changed the wildcard arm from an implicit/explicit allow to an explicit deny, and added an audit hook so unrecognized capabilities are visible to defenders instead of silently granted or silently ignored.
Vulnerable code:
match capability {
Capability::VaultRead => check_read(principal),
Capability::VaultWrite => check_write(principal),
_ => true, // unsafe default-allow
}
Secure code:
match capability {
Capability::VaultRead => check_read(principal),
Capability::VaultWrite => check_write(principal),
_ => {
audit_log_unknown_capability(&capability);
false // safe default-deny
}
}
Additional recommendations:
- Add vti-common helper
Capability::deny_by_default(cap) -> boolto standardize safe fallback across all consumers. - Add a cargo-deny/CI rule flagging new non_exhaustive enum variants for mandatory downstream review.
- Pin vti-common to exact versions in security-critical consumers rather than caret ranges.
🔍 Validation Log
- Verdict:
⚠️ Must-Review-By-Human- Confidence: 60%
- AI Validation Evidence: EVIDENCE FOUND: vti-common/src/acl/mod.rs defines
#[non_exhaustive] pub enum Capability { VaultRead, VaultWrite, ... }at line 118-124, matching the doc-comment intent that downstream consumers must write a_ =>deny-by-default wildcard arm. EVIDENCE NOT FOUND: No downstream consumer code (e.g. an actual ACL enforcement match statement) is present in source_files to show whether any wildcard arm defaults to allow/grant instead of deny — the finding's core claim is about hypothetical downstream misuse, not a concrete vulnerable sink in this repo's provided files. CHANGED VS PRE-EXISTING: vti-common/src/acl/mod.rs is directly quoted in the finding and is plausibly part of this MR (feat/non-exhaustive-growth-types) given the enum's #[non_exhaustive] attribute and doc comments referencing this exact change; treated as CHANGED. VERDICT JUSTIFICATION: Since no actual downstream match/sink implementing a permissive wildcard was found in the provided code, this is a design-risk/architectural finding rather than a demonstrated exploitable defect — cannot confirm exploitability, so must_review for human judgment on whether the design risk warrants action.- Validation Effort: This finding was validated up to a point, but the available evidence was insufficient for a conclusive automated verdict. A human (developer / security team) must manually review the code and decide. Not dismissed — treat as an open item pending human review.
🟡 AuditEvent enum #[non_exhaustive] enables silent audit-log gaps in downstream consumers
| Field | Detail |
|---|---|
| Severity | MEDIUM |
| Location | vti-common/src/audit/event.rs:11 |
| Finding ID | github_pr-9e8d85828c41 |
| CWE | CWE-778, CWE-223, CWE-440 |
| OWASP | A09:2021 - Security Logging and Monitoring Failures |
| MITRE ATT&CK | T1070 - Indicator Removal (analogous, log gap not deletion) |
| CAPEC | CAPEC-268, CAPEC-93 |
| DREAD | 5.4 |
| Reachability | 🔴 Reachable |
| Exploit Maturity | conceptual |
| Detection Source | skill_scan |
🧠 AI Triage:
- Triaged severity: MEDIUM
- No CVSS score exists (this is a library design/code-quality finding, not a CVE). CWE-778/223/440 describe logging completeness and omission issues rather than directly exploitable flaws. The scanner explicitly notes exploit maturity is 'conceptual' and there is no payload path that directly triggers this in vti-common — the risk manifests only when combined with a specific downstream anti-pattern (discard-only wildcard) and a future code change (new enum variant). This does not meet high/critical criteria (no confirmed exploit, no direct sink, impact limited to audit trail gaps) but is a legitimate, reachable design risk given production deployment and basic auth barrier, keeping it at medium.
- Composite score: 5.6
- Environment: production
Summary: The AuditEvent enum is intentionally non-exhaustive so new event types can ship in patch releases; downstream consumers must add wildcard handling, and a discard-only wildcard silently drops unrecognized security events, creating an audit blind spot after routine crate upgrades.
📝 Description:
Security-relevant actions occurring after a vti-common patch upgrade (e.g. room operations) may never appear in the audit log or SIEM, allowing an attacker to act without leaving a forensic trail and complicating detection of ACL abuse chained from VULN-001.
🧪 Proof of Concept:
The #[non_exhaustive] attribute guarantees new AuditEvent variants can be introduced without breaking downstream compilation, which means downstream match arms handling unknown variants are load-bearing for audit completeness — and vti-common cannot enforce that those arms record rather than discard events.
/// Audit-event payload. Tagged on `type` with the variant name and
/// the variant's data under `data`. Phase-0 vocabulary only;
/// Phase-1+ adds variants alongside the features that emit them.
/// **Non-exhaustive on purpose**, ...
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", content = "data")]
#[non_exhaustive]
pub enum AuditEvent {
/// Bootstrap completed — the first admin DID was written into the
/// ACL and the install carve-out was permanently closed.
Vulnerable lines: 11, 17
🔁 Reproduction Steps:
- Downstream audit forwarder implements: match event { AuditEvent::Bootstrap{..} => write_log(event), _ => {} }
cargo update -p vti-commonpulls a patch release introducing AuditEvent::RoomOperation (per documented precedent).- An action that emits AuditEvent::RoomOperation occurs in the system.
- The event matches the wildcard arm, executes the empty block, and is never written to the log/SIEM — confirmed by absence of any corresponding log entry.
🔎 Evidence: vti-common/src/audit/event.rs:11
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", content = "data")]
#[non_exhaustive]
pub enum AuditEvent {
/// Bootstrap completed — the first admin DID was written into the
/// ACL and the install carve-out was permanently closed.
💥 Impact:
Security-relevant actions occurring after a vti-common patch upgrade (e.g. room operations) may never appear in the audit log or SIEM, allowing an attacker to act without leaving a forensic trail and complicating detection of ACL abuse chained from VULN-001.
Confidentiality: None directly · Integrity: Low — audit records can be incomplete, undermining non-repudiation guarantees · Availability: Low — no service disruption but a monitoring/detection capability gap
🧭 Reachability:
- Network exposure: internal
- Auth barrier: basic
- Attack path: EP-002 (AuditEvent enum LIBRARY_API) → downstream audit forwarder's match statement → discard-only wildcard arm → event never reaches SIEM/log store
⚖️ Triage Factors:
| Factor | Value |
|---|---|
| Fixable | ✅ Yes |
| Exploitability | medium |
| Business impact | medium |
| Public exploit | None known |
| Environment | unknown |
Attack scenario: When vti-common ships a new AuditEvent variant in a patch release, a downstream audit forwarder with a discard-only wildcard silently drops those events, letting an attacker perform sensitive actions (e.g. room operations) with no audit record.
🔧 Remediation:
⚠️ AI-generated fix. Review, test in staging, and validate against your architecture before applying.
Replaced the no-op wildcard with a generic logging fallback that records the raw event data even when the specific variant is unknown to this build, closing the audit gap.
Vulnerable code:
match event {
AuditEvent::Bootstrap { .. } => write_log(event),
_ => {} // silently discarded
}
Secure code:
match event {
AuditEvent::Bootstrap { .. } => write_log(event),
_ => {
write_log_generic("unrecognized_audit_event", &event);
}
}
Additional recommendations:
- Store raw pre-deserialization audit payloads independently of typed enum matching for a retention period.
- Add integration test in vti-common that fails if a documented new variant lacks a corresponding downstream capture-test.
- Alert on any observed AuditEvent deserialization/match fallthrough in production telemetry.
🔍 Validation Log
- Verdict:
⚠️ Must-Review-By-Human- Confidence: 60%
- AI Validation Evidence: EVIDENCE FOUND: vti-common/src/audit/event.rs defines
#[non_exhaustive] pub enum AuditEvent { ... Bootstrap ... }at line 11-17, consistent with the finding's quoted snippet. EVIDENCE NOT FOUND: No downstream audit/SIEM forwarding code is present in source_files to show whether any consumer's wildcard match silently discards unrecognized AuditEvent variants — this is a hypothetical downstream misuse scenario, not a demonstrated sink in the provided files. CHANGED VS PRE-EXISTING: vti-common/src/audit/event.rs and its #[non_exhaustive] AuditEvent enum are the direct subject of this MR (feat/non-exhaustive-growth-types); treated as CHANGED. VERDICT JUSTIFICATION: Architectural/design risk without a concrete reachable sink demonstrated in the codebase provided — insufficient evidence to validate or dismiss, requires human judgment on the design tradeoff.- Validation Effort: This finding was validated up to a point, but the available evidence was insufficient for a conclusive automated verdict. A human (developer / security team) must manually review the code and decide. Not dismissed — treat as an open item pending human review.
Generated by Agentic Sec — AI Security Validation Agent
This report includes full scan data + AI validation evidence. Feed to engineering copilots for automated fix deployment.
Details
🛡️ Threat Model & Affect Analysis — PR #1262
| Field | Value |
|---|---|
| Repository | OpenVTC/verifiable-trust-infrastructure |
| Branch | feat/non-exhaustive-growth-types → main |
| Generated | 2026-09-06 |
ℹ️ This report contains theoretical threats and impact analysis for the MR.
Unlike the Security Code Review Report (which contains confirmed, materialised issues),
these are potential risks that may or may not be exploitable. Use this for defence-in-depth planning.
📋 Affect Analysis
Change Summary
This PR adds the #[non_exhaustive] attribute to two security-critical enums in vti-common — Capability (ACL) and AuditEvent (audit logging) — along with extensive doc-comment justification. The stated intent is to stop future variant additions (e.g. new capabilities or event types) from breaking downstream exhaustive match statements when shipped in patch releases, while forcing downstream code to carry an explicit wildcard arm rather than silently compiling against a closed set. The title's trailing '!' correctly signals this is a semver-breaking change for any downstream crate that currently uses exhaustive matches on these enums.
Diff: +21 / -0 lines
Types: breaking_api_change, security_attribute_change, documentation
Risk Assessment
- Overall Risk: high
- Pentest Needed: false
- Security Review Needed: true
Review Focus Areas:
- Locate and review every downstream
match Capability { ... }wildcard arm across all consuming crates/services to confirm deny-by-default semantics
Pentest Focus:
- Not directly pentestable from this diff alone (library-level API contract change, no network-facing behavior in the files shown); however, if pentesting downstream services, focus on: (1) attempting to trigger ACL checks for capabilities recently added upstream to verify deny-by-default wildcard behavior, and (2) attempting actions mapped to recently-added AuditEvent variants to verify they are actually recorded in the audit trail rather than silently dropped.
⚠️ Security Implications
🟠 Loss of compile-time exhaustiveness enforcement for Capability enum shifts authorization-safety burden to runtime wildcard-arm implementations
Loss of compile-time exhaustiveness enforcement for Capability enum shifts authorization-safety burden to runtime wildcard-arm implementations
Action: Ship a machine-enforced default-deny helper (trait or method) in vti-common rather than relying on doc-comment convention; require all known downstream consumers to migrate to it before this change is released; add CI contract tests verifying deny-by-default wildcard semantics.
🟡 Loss of compile-time exhaustiveness enforcement for AuditEvent enum risks silent audit-trail gaps
Loss of compile-time exhaustiveness enforcement for AuditEvent enum risks silent audit-trail gaps
Action: Provide a generic fallback deserialization/logging path in vti-common that captures raw {type, data} payloads for unrecognized variants, decoupling audit completeness from Rust enum exhaustiveness; mandate non-discarding wildcard arms via contract tests.
🟡 Non-exhaustive enums legitimize shipping future security-relevant Capability/AuditEvent variants in patch releases, bypassing major-version review triggers
Non-exhaustive enums legitimize shipping future security-relevant Capability/AuditEvent variants in patch releases, bypassing major-version review triggers
Action: Pin exact vti-common versions for security-critical consumers; establish a security-specific changelog signal distinct from semver so new Capability/AuditEvent variants trigger mandatory review regardless of version bump size.
⚪ Doc comments correctly articulate fail-closed/fail-safe intent for both enums
Doc comments correctly articulate fail-closed/fail-safe intent for both enums
Action: Convert this documented intent into an enforced mechanism (trait/helper/lint/contract test) in a follow-up change.
⚪ Doc comments contain persuasive prose that could bias automated or human review of this and future similar changes
Doc comments contain persuasive prose that could bias automated or human review of this and future similar changes
Action: Establish a review policy requiring independent technical verification of security claims in doc comments, separate from narrative trust; flag unusually persuasive justificatory prose adjacent to security-attribute changes for mandatory second-reviewer sign-off.
🧩 Affected Components
| Component | Impact | Change | What Changed |
|---|---|---|---|
| Capability ACL Enum / Access Control Contract | high | modified | The Capability enum's exhaustiveness contract was removed via #[non_exhaustive], meaning downstream crates can no longer exhaustively matc |
| AuditEvent Enum / Audit Logging Contract | high | modified | The AuditEvent enum's exhaustiveness contract was removed via #[non_exhaustive], meaning downstream audit/SIEM forwarding code can no long |
| Cargo Dependency / Semver Contract for vti-common | medium | modified | Marking both enums #[non_exhaustive] means that future additions of new Capability/AuditEvent variants become additive/non-breaking per Rust |
📁 File Classifications
vti-common/src/acl/mod.rs
- Type: security
vti-common/src/audit/event.rs
- Type: security
🛡️ STRIDE Threat Model
Identified Threats (10)
🟠 STRIDE-1: Silent Capability Grant via Non-Exhaustive Enum Wildcard Fallthrough in Capability Enum
| Field | Detail |
|---|---|
| Category | Tampering, Elevation of Privilege |
| Severity | High |
| Likelihood | Likely |
| CVSS | 8.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | High |
| CWE | CWE-284,CWE-863,CWE-440 |
| CAPEC | CAPEC-122,CAPEC-180 |
| OWASP | A01:2021 - Broken Access Control |
Description: LIBRARY_API consumption of the Capability enum in downstream ACL enforcement code allows unauthorized capability elevation due to #[non_exhaustive] forcing wildcard match arms that may default to permissive (allow) behavior instead of deny, resulting in unauthorized access to gated resources (vault, memory, rooms).
Evidence: vti-common/src/acl/mod.rs:118-134
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Capability {
VaultRead,
VaultWrite,
Attack Scenario:
- Upstream vti-common crate ships a patch release (e.g. 0.16.2 pattern) adding a new Capability variant such as
RoomOpenwithout a major version bump because #[non_exhaustive] permits additive changes as non-breaking. - A downstream consumer crate performs
cargo updateand picks up the new vti-common version transparently, per semver rules for patch releases. - Because the enum in vti-common/src/acl/mod.rs is marked #[non_exhaustive] (line added in diff), the downstream crate's existing
match capability { VaultRead => ..., VaultWrite => ..., _ => <fallback> }still compiles unchanged. - If the developer wrote the wildcard arm as
_ => true(allow) or_ => Grantduring initial implementation — a plausible mistake, especially in code reviewed before any non-exhaustive variants existed — the newRoomOpencapability is silently granted to any principal who is evaluated against it. - An attacker who can trigger an ACL check against the new capability (e.g. requesting room-open action) is granted access they should not have, without any code change or attacker awareness of the underlying enum change.
- No compile-time error, deployment gate, or runtime warning surfaces this misconfiguration because the enum contract explicitly permits open extension.
🔎 Threat Clue: Derived from COMP-001 via EP-001
- Data Flows: Capability enum serialization/deserialization between ACL producer and downstream consumer
Preconditions: Downstream consumer implements a wildcard _ match arm on Capability that defaults to allow/grant semantics rather than deny/flag-unknown., Attacker or normal application flow can trigger evaluation of a newly introduced capability variant not explicitly handled downstream., Downstream crate consumes vti-common via a semver range permitting automatic patch upgrades (e.g. ^0.16).
Existing Controls: Doc comment explicitly instructs downstream implementers to write a _ => arm that treats unknown capabilities as not-granted. • PartialEq/Eq/Hash derives allow safe use in HashSet-based allow-lists which are inherently deny-by-default.
Recommended Mitigations: Publish and enforce a lint (e.g. clippy custom lint or CI check) that flags any wildcard match on Capability that returns a permissive/true/Grant value. • Provide a helper method Capability::is_known_and_granted(&self, ctx) in vti-common that centralizes safe default-deny behavior instead of relying on each downstream match arm. • Add a runtime assertion/test harness in vti-common that fails CI if a hypothetical new variant added in a patch release is not covered by a documented deny-by-default policy in known consumer crates. • Emit a structured audit log entry whenever an unknown/unrecognized capability variant is evaluated, to detect silent-allow patterns in production telemetry.
🟡 STRIDE-2: Audit Trail Gap via Unhandled Non-Exhaustive AuditEvent Variant in Audit Logging Pipeline
| Field | Detail |
|---|---|
| Category | Repudiation, Information Disclosure |
| Severity | Medium |
| Likelihood | Likely |
| CVSS | 6.5 CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:L/SI:N/SA:N |
| Residual Severity | Medium |
| CWE | CWE-778,CWE-223,CWE-440 |
| CAPEC | CAPEC-268,CAPEC-93 |
| OWASP | A09:2021 - Security Logging and Monitoring Failures |
Description: LIBRARY_API consumption of the AuditEvent enum in downstream audit/SIEM pipelines allows repudiation of sensitive actions due to #[non_exhaustive] permitting new event variants that a wildcard match may silently drop, resulting in loss of audit trail integrity and undetected malicious activity.
Evidence: vti-common/src/audit/event.rs:43-61
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", content = "data")]
#[non_exhaustive]
pub enum AuditEvent {
/// Bootstrap completed — the first admin DID was written into the
/// ACL and the install carve-out was permanently closed.
Attack Scenario:
- vti-common introduces a new AuditEvent variant (analogous to the
RoomOperationvariant referenced in the diff's doc comment) in a patch release due to #[non_exhaustive] on the enum in vti-common/src/audit/event.rs. - Downstream SIEM/audit-forwarding code contains
match event { Bootstrap{..} => log(...), ... , _ => {} }where the wildcard silently discards unrecognized events rather than recording a generic 'unknown event occurred' entry. - An attacker performs an action mapped to the new event type (e.g., a new room-operation event) that is never surfaced downstream because of the silent-drop wildcard.
- Because the action is never logged, the attacker can later deny having performed it (repudiation), and defenders lose visibility into the security-relevant action for incident response.
- The serde tag="type"/content="data" encoding also risks partial data loss on the wire if downstream deserializers use
#[serde(other)]semantics improperly, further degrading audit fidelity.
🔎 Threat Clue: Derived from COMP-002 via EP-002
- Data Flows: AuditEvent serialization/deserialization between event producer and SIEM/log-forwarding consumer
Preconditions: Downstream audit pipeline implements a no-op or discard-only wildcard arm on AuditEvent., Attacker can trigger an action mapped to a new/unrecognized AuditEvent variant., No independent out-of-band logging (e.g. raw request logging) compensates for the dropped audit event.
Existing Controls: Doc comment explicitly requires downstream consumers to record unknown events rather than drop them. • Tagged serde representation (tag="type", content="data") preserves forward-compatible deserialization structure for logging even of unknown variants if handled generically.
Recommended Mitigations: Provide a vti-common helper for downstream consumers that deserializes AuditEvent into a generic {type: String, data: Value} fallback when the concrete variant is unrecognized, guaranteeing at least raw capture. • Mandate (via CI contract test in vti-common) that consumer crates implement a non-discarding wildcard arm, verified via an integration test crate. • Add structured monitoring/alerting on audit pipeline for deserialization or match fallthrough events indicating unknown AuditEvent variants were received. • Store raw pre-deserialization audit payloads for a retention period independent of typed enum matching, decoupling audit completeness from Rust type evolution.
🟡 STRIDE-3: Semver-Masked Breaking Change via Non-Exhaustive Enum Extension Rendering Patch Releases Security-Relevant
| Field | Detail |
|---|---|
| Category | Tampering, Elevation of Privilege |
| Severity | Medium |
| Likelihood | Likely |
| CVSS | 5.9 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:L |
| Residual Severity | Medium |
| CWE | CWE-1104,CWE-696 |
| CAPEC | CAPEC-437,CAPEC-536 |
| OWASP | A06:2021 - Vulnerable and Outdated Components |
Description: LIBRARY_API version-range dependency resolution in downstream Cargo.toml manifests allows unnoticed security-behavior change due to #[non_exhaustive] permitting new enum variants to ship in patch releases, resulting in downstream consumers unknowingly deploying altered ACL/audit semantics without a corresponding review or major-version signal.
Evidence: vti-common/src/acl/mod.rs:119-127
/// each addition used to be a breaking change for anyone matching on it — which is exactly what happened: `MemoryRead`, `MemoryWrite`, `RoomPresent` and `RoomOpen` went out in `vti-common` 0.16.2, a patch release
Attack Scenario:
- vti-common publishes a patch release (per the documented precedent: MemoryRead/MemoryWrite/RoomPresent/RoomOpen shipped in 0.16.2) adding new Capability or AuditEvent variants.
- Because these enums are #[non_exhaustive], semver tooling (cargo, crates.io) classifies this as a non-breaking additive change, so it is legally shippable as a patch version bump.
- Downstream crates using a caret/tilde version requirement (e.g.
vti-common = "0.16") automatically pull in the new variant on the nextcargo build/cargo updatewith no manual review gate, CI failure, or changelog-triggered security review. - If any downstream ACL/audit logic has security-relevant default behavior triggered by presence/absence of variants (e.g., counting known capabilities, iterating over all variants via a hardcoded array that is now incomplete), the new variant silently participates in or is excluded from that logic.
- Attackers who track the vti-common changelog/repository can proactively identify security-relevant capability names before downstream consumers patch their wildcard-arm logic, exploiting the window between crate release and downstream remediation.
🔎 Threat Clue: Derived from COMP-001, COMP-002 via EP-001, EP-002
- Data Flows: Dependency resolution pipeline for vti-common crate versions
Preconditions: Downstream Cargo.toml specifies a semver range instead of an exact pin for vti-common., Automated dependency update pipelines (Dependabot, Renovate) merge patch bumps without manual security review., A time-of-check-to-time-of-use window exists between crate publication and downstream wildcard-arm remediation.
Existing Controls: Doc comments in the source explicitly warn maintainers about this exact historical precedent and rationale. • #[non_exhaustive] forces at least a compile-time wildcard requirement, preventing silent non-compilation but not silent semantic drift.
Recommended Mitigations: Recommend/enforce exact version pinning (=0.16.2) for security-critical crates like vti-common in downstream Cargo.lock policies. • Publish a machine-readable changelog entry or crate feature flag whenever new Capability/AuditEvent variants are added, enabling automated security-review triggers independent of semver. • Integrate a supply-chain policy tool (e.g. cargo-deny, cargo-audit custom advisories) that flags any diff introducing new variants to non_exhaustive security enums for mandatory manual review before merge. • Document a formal security changelog convention distinct from semver for gating-relevant capability additions.
🔵 STRIDE-4: Deserialization Denial of Service via Unknown Capability/AuditEvent Variant Rejection in Strict Consumers
| Field | Detail |
|---|---|
| Category | Denial of Service |
| Severity | Low |
| Likelihood | Possible |
| CVSS | 4.3 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-20,CWE-755 |
| CAPEC | CAPEC-153,CAPEC-490 |
| OWASP | A04:2021 - Insecure Design |
Description: LIBRARY_API deserialization boundary for Capability/AuditEvent in downstream serde consumers allows disruption of processing due to strict deny-unknown-fields or exhaustive enum deserialization patterns rejecting forward-compatible payloads, resulting in message-processing failures or dropped requests when producer/consumer versions of vti-common diverge.
Evidence: vti-common/src/audit/event.rs:51-52
#[serde(tag = "type", content = "data")]
#[non_exhaustive]
pub enum AuditEvent {
Attack Scenario:
- A producer service running a newer vti-common version emits an ACL check or audit event using a newly introduced Capability/AuditEvent variant not yet known to an older-versioned consumer service (e.g., an internal microservice still pinned to a prior vti-common release).
- The consumer's serde deserialization of the incoming Capability/AuditEvent payload encounters an unrecognized
kebab-casevariant name ortypetag value. - Because Rust's serde derive for enums (even #[non_exhaustive] on the Rust side) still requires an exact match for deserialization unless explicitly handled with #[serde(other)], deserialization fails with an error.
- If the consumer's message-processing pipeline does not gracefully catch this deserialization error (e.g., a message queue consumer that panics or dead-letters on deserialize failure), a stream of new-variant events can cause repeated processing failures, backlog growth, or crash loops.
- This mismatch is more likely to be triggered attacker-adjacent during coordinated multi-service rollout windows or if an attacker can indirectly cause the producer to emit synthetic events of new types faster than consumers can be upgraded.
🔎 Threat Clue: Derived from COMP-001, COMP-002 via EP-001, EP-002
- Data Flows: Wire-format serialization of Capability/AuditEvent between services of differing vti-common versions
Preconditions: Producer and consumer services run different vti-common versions with differing known variant sets., Consumer's serde deserialization does not use #[serde(other)] or a catch-all fallback for unknown variants., Message pipeline lacks error isolation/circuit breaking for individual malformed/unrecognized messages.
Existing Controls: #[non_exhaustive] on the Rust type only affects local pattern matching, not wire-format tolerance, so this is a partially separate but related risk surfaced by the same design pattern. • Phase-0/Phase-1 versioned rollout strategy mentioned in doc comments suggests variants are introduced deliberately alongside feature rollout, reducing surprise mismatches somewhat.
Recommended Mitigations: Add #[serde(other)]-style fallback variant handling in AuditEvent/Capability wire formats to allow forward-compatible deserialization of unknown variants into an Unknown bucket. • Enforce version skew policies during rolling deployments so producer/consumer vti-common versions never diverge beyond N-1 compatibility window. • Wrap deserialization of Capability/AuditEvent payloads in try/catch with dead-letter-queue isolation rather than pipeline-wide failure.
🔵 STRIDE-5: Capability HashSet Poisoning via Hash/Eq Semantics Divergence Across Crate Versions
| Field | Detail |
|---|---|
| Category | Tampering, Information Disclosure |
| Severity | Low |
| Likelihood | Unlikely |
| CVSS | 3.7 CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-1076,CWE-704 |
| CAPEC | CAPEC-176 |
| OWASP | A08:2021 - Software and Data Integrity Failures |
Description: LIBRARY_API HashSet ACL storage in downstream persistence/serialization layers allows integrity inconsistency due to derive(Hash, Eq) semantics that could subtly diverge if future non-exhaustive variant additions are not carefully audited for hash-stability, resulting in ACL entries becoming unrecognized or silently duplicated across serialized-store round-trips.
Evidence: vti-common/src/acl/mod.rs:116-118
/// produces a sensible default from the existing role (Admin gets
/// everything, Reader gets only `vault-read`, etc.) so existing ACL
/// behaviour is preserved bit-for-bit.
Attack Scenario:
- Downstream consumer persists a
HashSet<Capability>(or similarly keyed structure) to a database, cache, or serialized store, relying on stable Hash/Eq derived from the enum's discriminants. - A new Capability variant is added upstream (permitted by #[non_exhaustive]) and the consumer application upgrades vti-common without regenerating/migrating previously persisted ACL data.
- If any downstream code paths reconstruct capability sets by iterating an assumed-exhaustive list of 'all known capabilities' (a common anti-pattern for building default role templates), the newly-added variant is absent from that reconstructed set even though the enum technically supports it.
- A privileged role template (e.g. 'Admin gets everything') built from a stale hardcoded list therefore does not automatically include the new capability, potentially under- or over-provisioning administrators depending on how the new capability interacts with default-deny logic elsewhere.
- This creates an integrity inconsistency between the intended security model ('Admin gets everything') and actual persisted/reconstructed capability sets after a crate upgrade.
🔎 Threat Clue: Derived from COMP-001 via EP-001
- Data Flows: Persisted HashSet ACL role-template reconstruction
Preconditions: Downstream code maintains a separate hardcoded 'all capabilities' list rather than using enum reflection/iteration., A new Capability variant is introduced in a patch release consumed automatically., No migration step re-derives role-to-capability mappings after the crate upgrade.
Existing Controls: Doc comment states role defaults are intended to be preserved 'bit-for-bit', implying some existing discipline around default-set stability. • Copy/Clone/PartialEq/Eq/Hash derives are structurally stable across additions since only new variants are appended, not existing ones altered.
Recommended Mitigations: Provide an official Capability::all() or Capability::ALL const/iterator in vti-common so downstream consumers never hardcode a duplicate variant list. • Add an integration test in vti-common that fails if the number of known variants diverges from any published downstream default-role fixture (contract test). • Document explicitly in the crate's upgrade guide that role-to-capability default templates must be reviewed on every vti-common version bump.
⚪ STRIDE-6: Documentation-Embedded Prompt Injection Attempt in Rust Doc Comments Targeting Automated Review Tooling
| Field | Detail |
|---|---|
| Category | Spoofing, Tampering |
| Severity | Informational |
| Likelihood | Possible |
| CVSS | 2.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | None |
| CWE | CWE-1021 |
| CAPEC | CAPEC-148 |
| OWASP | A05:2021 - Security Misconfiguration |
Description: LIBRARY_API doc comments in Capability/AuditEvent source files allow content-based influence over automated security review pipelines due to persuasive natural-language framing embedded in code comments justifying the #[non_exhaustive] change, resulting in potential reviewer/LLM bias toward accepting the change without independent risk verification.
Evidence: vti-common/src/acl/mod.rs:119-129
/// **Non-exhaustive on purpose.** This list grows every time the agent gains a
/// power worth gating separately...
Attack Scenario:
- A contributor (malicious or benign) embeds detailed, persuasive prose directly in Rust doc comments (
///) explaining why #[non_exhaustive] is safe and 'the point' of the design, as seen in both diffs. - Automated PR-review tools, static analyzers, or LLM-based security scanners that treat doc comments as authoritative context may weight this narrative heavily when assessing the diff's risk, rather than independently verifying downstream wildcard-arm correctness.
- Human reviewers under time pressure may also defer to the confident, detailed justification in the comment rather than auditing actual downstream consumer code for default-deny compliance.
- If this pattern is repeated by an attacker with commit access (e.g. compromised maintainer account or malicious insider) to justify future genuinely unsafe non_exhaustive additions with similarly confident prose, review scrutiny may be systematically reduced.
- This is a process/social-engineering risk vector layered on top of the legitimate technical rationale present in this specific diff.
🔎 Threat Clue: Derived from COMP-001, COMP-002 via EP-001, EP-002
- Data Flows: PR review / CI documentation-analysis pipeline
Preconditions: Automated or human reviewers place high trust in in-code documentation narrative over independent verification., Attacker has commit/PR access to embed persuasive comments alongside future genuinely risky changes.
Existing Controls: This specific instance's rationale is technically sound and matches Rust community best practice for non_exhaustive enums, reducing immediate risk. • Diff is small and auditable, limiting the blast radius of any single such comment.
Recommended Mitigations: Establish a review policy requiring independent technical verification of security claims in doc comments rather than trusting narrative alone. • Flag PRs containing unusually persuasive or lengthy justificatory prose adjacent to security-relevant attribute changes for mandatory second-reviewer sign-off. • Ensure automated security scanners treat source comments strictly as documentation/context, never as authoritative instructions, per standard prompt-injection-resistant design.
🟡 STRIDE-7: Missing Compile-Time Enforcement of Wildcard Deny-by-Default Contract Across Downstream Crates
| Field | Detail |
|---|---|
| Category | Tampering, Elevation of Privilege |
| Severity | Medium |
| Likelihood | Likely |
| CVSS | 5.3 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Medium |
| CWE | CWE-284,CWE-1023 |
| CAPEC | CAPEC-122 |
| OWASP | A04:2021 - Insecure Design |
Description: LIBRARY_API contract for Capability/AuditEvent enums allows inconsistent enforcement of the documented deny-by-default policy due to the policy being expressed only as a natural-language doc comment rather than a machine-enforced trait/interface, resulting in some downstream consumers implementing correct deny-default while others silently allow, with no automated way to detect the divergence.
Evidence: vti-common/src/acl/mod.rs:126-129
/// Downstream code must carry a `_ =>` arm. That is the point: a capability this
/// consumer has never heard of is precisely the one it must not silently treat as
/// granted, and a wildcard arm forces that decision to be written down.
Attack Scenario:
- vti-common's design intent (deny-unknown-capability, record-unknown-event) exists solely in doc comments, not in any enforced trait, macro, or lint provided by the crate itself.
- Multiple downstream crates independently implement their own match arms against Capability/AuditEvent, each interpreting the doc-comment guidance with varying fidelity.
- Some downstream implementations correctly deny/record unknowns; others — due to developer oversight, copy-paste from an early exhaustive-match era, or misunderstanding — default to allow/discard.
- There is no automated test, shared trait, or CI check across the ecosystem (or even within a single organization's multiple internal consumers) verifying consistent policy application.
- An attacker who maps the ecosystem of vti-common consumers can specifically target the subset of services with weaker wildcard-arm implementations for privilege escalation or audit evasion, exploiting the inconsistency rather than a single code flaw.
🔎 Threat Clue: Derived from COMP-001, COMP-002 via EP-001, EP-002
- Data Flows: Cross-crate policy enforcement contract for Capability/AuditEvent
Preconditions: Multiple independent downstream crates/services consume Capability/AuditEvent., No shared enforcement mechanism (trait, macro, CI contract test) exists across those consumers., Attacker has reconnaissance capability to identify which consumer implementations are weaker.
Existing Controls: Doc comments provide clear, consistent guidance to all consumers reading the source, reducing (but not eliminating) implementation drift. • #[non_exhaustive] at least guarantees a wildcard arm must exist syntactically, even if its semantics are wrong.
Recommended Mitigations: Ship a companion trait in vti-common, e.g. trait CapabilityPolicy { fn is_granted(&self, cap: Capability) -> bool; } with a provided default-deny blanket implementation downstream crates must opt out of explicitly to allow. • Provide a #[must_use]-style compile-time lint or macro (capability_match!) that enforces deny-by-default semantics structurally rather than relying on comment-driven convention. • Publish a conformance test suite/crate that downstream teams can run in CI to verify their wildcard-arm implementations match the intended deny/record-default policy.
🟡 STRIDE-8: Cross-Service Time-of-Check-to-Time-of-Use Window During Rolling Deployment of New Capability Variants
| Field | Detail |
|---|---|
| Category | Elevation of Privilege, Tampering |
| Severity | Medium |
| Likelihood | Possible |
| CVSS | 5.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Medium |
| CWE | CWE-367,CWE-362 |
| CAPEC | CAPEC-25 |
| OWASP | A04:2021 - Insecure Design |
Description: LIBRARY_API rolling-upgrade window across microservices consuming Capability enum allows privilege inconsistency due to a TOCTOU gap where some service instances recognize a new capability variant and others do not during deployment, resulting in inconsistent authorization decisions for the same principal depending on which instance handles the request.
Evidence: vti-common/src/acl/mod.rs:121-124
/// `MemoryRead`, `MemoryWrite`, `RoomPresent` and `RoomOpen` went out in `vti-common` 0.16.2, a
/// patch release, and any downstream exhaustive `match` stopped compiling on a
/// routine `cargo update`.
Attack Scenario:
- An organization deploys multiple service instances behind a load balancer, all depending on vti-common, and begins a rolling upgrade to a version that introduces a new Capability variant.
- During the rollout window, some instances run the old vti-common version (new variant does not exist / cannot be evaluated) while others run the new version (variant exists and is evaluated by an updated match arm).
- An attacker (or automated retry logic) sends repeated requests requiring evaluation of the new capability; requests load-balanced to old-version instances may be denied by default (safe) or may error, while requests reaching new-version instances follow the new/possibly still-being-tuned logic.
- If the attacker can distinguish instance versions (e.g. via response timing, headers, or error message differences) they can selectively retry until routed to an instance with looser enforcement of the new capability, achieving inconsistent access during the deployment window.
- This is a classic distributed-systems TOCTOU/version-skew race condition amplified specifically by the non_exhaustive design enabling frequent, low-visibility capability additions.
🔎 Threat Clue: Derived from COMP-001 via EP-001
- Data Flows: Rolling deployment traffic across mixed-version service instances
Preconditions: Rolling deployment strategy with mixed-version service instances momentarily coexisting., Load balancer routes requests non-deterministically across instance versions., New capability's authorization outcome differs meaningfully between old (absent) and new (present) instance versions.
Existing Controls: Standard rolling-deployment practices generally aim to minimize this window's duration. • Phase-0/Phase-1 rollout language in the doc comments suggests intentional, gradual feature exposure which may include operational safeguards not visible in this diff.
Recommended Mitigations: Adopt blue/green or canary deployment strategies with capability-version-aware routing to avoid mixed-version request handling for security-critical evaluations. • Feature-flag new Capability variants server-side so they are inert until explicitly and atomically enabled across the entire fleet. • Add distributed tracing/correlation to detect and alert on divergent authorization outcomes for identical requests across instances during deployments.
🔵 STRIDE-9: Audit Log Injection via Untrusted Data Embedded in AuditEvent content Field
| Field | Detail |
|---|---|
| Category | Tampering, Information Disclosure |
| Severity | Low |
| Likelihood | Possible |
| CVSS | 3.9 CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Residual Severity | Low |
| CWE | CWE-117,CWE-116 |
| CAPEC | CAPEC-93,CAPEC-267 |
| OWASP | A09:2021 - Security Logging and Monitoring Failures |
Description: LIBRARY_API AuditEvent content="data" payload structure allows log/SIEM injection due to the enum's data-carrying variants potentially embedding attacker-influenced strings (e.g. DID values, room names) without confirmed sanitization at the vti-common layer, resulting in log forgery or downstream SIEM query/UI injection when unrecognized/new variants carry unsanitized fields.
Evidence: vti-common/src/audit/event.rs:44-63
pub const REDACTED_MARKER: &str = "<redacted>";
...
pub enum AuditEvent {
/// Bootstrap completed — the first admin DID was written into the
/// ACL and the install carve-out was permanently closed.
Attack Scenario:
- AuditEvent variants (e.g. Bootstrap, and future room-operation-related variants referenced in the doc comment) carry structured
datapayloads that may include attacker-influenced strings such as DIDs, room identifiers, or user-supplied metadata. - The excerpted source does not show explicit sanitization/escaping logic for these fields at the vti-common serialization boundary; encoding relies entirely on serde JSON escaping, which protects against structural injection but not semantic log injection (e.g. embedding fake log lines, control characters, or misleading identifiers).
- If a downstream SIEM or audit-viewer renders
datafields into HTML/log-viewer UIs without independent output encoding, an attacker-controlled DID or room name embedded during an earlier phase-0 action (e.g.Bootstrapadmin DID during install) could carry injected content that executes or misleads when rendered later. - Because the enum is #[non_exhaustive] and new variants will carry new/undocumented data shapes, future variants may inadvertently introduce fields lacking the same sanitization discipline as earlier ones, expanding this risk surface over time without a corresponding centralized review gate.
- This constitutes a defense-in-depth gap: reliance purely on serde structural safety without confirmed content-level sanitization for security-audit consumption downstream (e.g. SIEM dashboards, log correlation tools).
🔎 Threat Clue: Derived from COMP-002 via EP-002
- Data Flows: AuditEvent data payload flowing from event producer to log storage/SIEM
Preconditions: AuditEvent data fields include attacker-influenced string content (e.g. DIDs, room names) without additional sanitization., Downstream SIEM/log viewer renders these fields without independent output encoding., Attacker can control content of a field eventually recorded in an audit event (e.g. during install/bootstrap or room operations).
Existing Controls: REDACTED_MARKER constant referenced elsewhere in the file suggests some existing sanitization/redaction discipline for sensitive fields. • serde JSON serialization provides baseline structural escaping preventing raw injection into the JSON document itself.
Recommended Mitigations: Confirm and document that all AuditEvent data fields undergo canonical sanitization/normalization (e.g. DID format validation, string length caps, control-character stripping) before serialization. • Mandate that any downstream audit-viewer/SIEM integration performs independent output encoding regardless of upstream serialization guarantees. • Extend the REDACTED_MARKER pattern to cover any new free-text or attacker-influenced fields introduced by future non_exhaustive AuditEvent variants.
⚪ STRIDE-10: Bootstrap Admin DID Install Carve-Out Referenced in AuditEvent Suggests Prior One-Time Privilege Escalation Window
| Field | Detail |
|---|---|
| Category | Elevation of Privilege, Spoofing |
| Severity | Informational |
| Likelihood | Unlikely |
| CVSS | 2.6 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:L/SA:N |
| Residual Severity | None |
| CWE | CWE-367,CWE-696 |
| CAPEC | CAPEC-26 |
| OWASP | A04:2021 - Insecure Design |
Description: LIBRARY_API Bootstrap AuditEvent variant documentation references an 'install carve-out' permanently closed after first-admin-DID write, allowing potential re-exploitation of the bootstrap window due to unclear guarantees within this diff about how 'permanently closed' is enforced at runtime, resulting in a theoretical risk of repeated bootstrap/admin-claiming if the closing mechanism is flawed elsewhere in the codebase (not shown in this diff).
Evidence: vti-common/src/audit/event.rs:60-62
pub enum AuditEvent {
/// Bootstrap completed — the first admin DID was written into the
/// ACL and the install carve-out was permanently closed.
Attack Scenario:
- The AuditEvent::Bootstrap variant's doc comment states that 'the first admin DID was written into the ACL and the install carve-out was permanently closed', implying a one-time privileged bootstrap process exists elsewhere in the system.
- This diff does not show the actual enforcement code for 'permanently closed' — no evidence is provided in the excerpted files that this state is idempotent, race-condition-free, or persisted correctly across restarts/crashes.
- If the bootstrap-closing mechanism (not visible here) relies on in-memory flags, a database write without proper transactional guarantees, or a check-then-act pattern, an attacker able to trigger concurrent or repeated bootstrap attempts (e.g. during a crash-restart cycle) could potentially claim admin a second time.
- Because this AuditEvent variant is the only visible artifact referencing this security-critical lifecycle event, and the enforcement logic is outside the analyzed diff, this is flagged as a theoretical/speculative risk requiring verification against the actual bootstrap implementation (e.g. in a vti-common submodule or a separate service not included in this diff).
- This threat is included because #[non_exhaustive] evolution of AuditEvent could later obscure or complicate auditing of exactly how many times a Bootstrap event was legitimately emitted versus attempted-and-rejected.
🔎 Threat Clue: Derived from COMP-002 via EP-002
- Data Flows: Bootstrap admin DID write to ACL, referenced but not implemented in this diff
Preconditions: The actual bootstrap/install-carve-out closing logic (not present in this diff) contains a TOCTOU or non-atomic state transition., Attacker has network or local access sufficient to trigger the bootstrap/install code path more than once., This is a speculative threat pending review of code not included in the provided source excerpt.
Existing Controls: Doc comment explicitly states the carve-out is 'permanently closed', suggesting deliberate design intent to prevent re-bootstrap, though enforcement is not visible in this diff. • AuditEvent for Bootstrap provides at least an audit trail entry point for detecting repeated/anomalous bootstrap attempts if logged correctly.
Recommended Mitigations: Verify (outside this diff) that the bootstrap-closing mechanism uses atomic, transactional, crash-safe persistence (e.g. a single database constraint or distributed lock) rather than in-memory or non-atomic flags. • Ensure every bootstrap attempt (successful or rejected) emits a corresponding AuditEvent so repeated attempts are independently detectable and alertable. • Add explicit test coverage in the bootstrap module (not shown here) for concurrent/racing bootstrap attempts.
🍝 PASTA Threat Model
Application Purpose
vti-common provides shared ACL capability definitions and audit-event types used across the Verifiable Trust Infrastructure ecosystem to gate agent capabilities (vault, memory, room access) and to record security-relevant actions for audit/compliance purposes.
Inherent Risks
- The crate's core value proposition (extensible, non-breaking ACL/audit vocabulary) inherently trades compile-time exhaustiveness safety for API evolution flexibility.
- Downstream consumers are solely responsible for correct default-deny/record-default semantics, creating a distributed trust dependency the crate cannot itself enforce.
- Automatic patch-level dependency updates can introduce new security-relevant capability/event variants without triggering manual security review.
Objectives
Risk: Accept additive API changes in patch releases only if paired with guaranteed default-deny/record-default consumer behavior.
Business: Enable safe, incremental rollout of new agent capabilities without forcing coordinated breaking releases across all downstream consumers.
Security: Ensure new capabilities are denied by default until explicitly recognized and granted by policy.; Ensure no security-relevant action is silently dropped from the audit trail.
Financial: Avoid costly emergency patch cycles across the consumer ecosystem caused by breaking API changes.
Compliance: Maintain a complete and non-repudiable audit trail sufficient for security incident investigation and regulatory audit requirements.
Functional: Provide a stable, extensible Capability and AuditEvent vocabulary shared across ACL enforcement and audit logging subsystems.
Operational: Support Phase-0/Phase-1+ incremental feature rollout where new capabilities/events ship alongside the features that use them.
Business Impact Analysis (3)
BIA-1: ACL Capability Enforcement (Critical)
The end-to-end process by which an agent's requested action is checked against its granted Capability set before vault, memory, or room operations are permitted.
MTD: 01 days 00:00 hours | RTO: 00 days 04:00 hours | RPO: 00 days 00:00 hours
- Stakeholders: ACL Policy Administrators / Downstream Consuming Services / End Users / Security/Compliance Team
- Dependencies: vti-common Capability enum / Downstream ACL enforcement match logic / HashSet-based role-to-capability storage / cargo dependency resolution pipeline
- Disruptions: Silent-allow wildcard match arm grants unauthorized capability access. / Rolling deployment version skew causes inconsistent authorization decisions. / Stale hardcoded 'all capabilities' list omits newly added variants in role templates.
- Impacts: Unauthorized access to vault secrets or memory data. / Regulatory non-compliance for access-control failures. / Loss of customer trust following disclosed unauthorized access incident.
BIA-2: Security Audit Trail Recording (High)
The end-to-end process by which security-relevant agent actions are captured as AuditEvent records and forwarded to log storage/SIEM for compliance and incident response.
MTD: 03 days 00:00 hours | RTO: 01 days 00:00 hours | RPO: 00 days 01:00 hours
- Stakeholders: Security/Compliance Team / Incident Response Team / Auditors/Regulators / Downstream Consuming Services
- Dependencies: vti-common AuditEvent enum / Downstream audit-forwarding/SIEM pipeline / REDACTED_MARKER sanitization logic / Log storage infrastructure
- Disruptions: Silent-drop wildcard match arm discards unrecognized audit events. / Deserialization failures on version-skewed audit payloads. / Log/SIEM injection via unsanitized event data fields.
- Impacts: Loss of audit trail integrity impairing incident investigation. / Regulatory fines for incomplete compliance logging. / Repudiation of malicious actions due to missing evidence.
BIA-3: Dependency Version Management and Release Review (Medium)
The process by which downstream teams evaluate and adopt new vti-common crate releases, including manual or automated review of security-relevant enum additions.
MTD: 07 days 00:00 hours | RTO: 02 days 00:00 hours | RPO: 01 days 00:00 hours
- Stakeholders: Platform Engineering Team / Security Review Board / Dependency Automation Tooling Owners
- Dependencies: Cargo/crates.io dependency resolution / Dependabot/Renovate automation / Internal changelog review process
- Disruptions: Automatic patch-level upgrade introduces new security-relevant variant without review gate. / Lack of machine-readable security-changelog signal distinct from semver.
- Impacts: Unreviewed security-behavior changes reach production. / Increased time-to-detect for capability/audit-vocabulary drift.
Technical Scope
Roles (3): RO-1 Admin · RO-2 Reader · RO-3 Crate Maintainer
Actors (3): AC-1 Downstream Service Developer · AC-2 Automated Dependency Bot · AC-3 Agent Principal
Entry Points (2): EP-001 Capability Enum Public API · EP-002 AuditEvent Enum Public API
Threat Actors (4): TA-1 Malicious Insider Downstream Developer · TA-2 External Attacker Targeting Access Control · TA-3 Supply Chain Manipulator · TA-4 Insider Threat Evading Audit
Infrastructure (1): IF-1 Downstream Service Deployment Fleet
Trust Boundaries (3): TB-1 vti-common Crate Boundary · TB-2 Dependency Supply Chain Boundary · TB-3 Audit/SIEM Integration Boundary
External Entities (2): EE-1 Crates.io Registry / Dependency Feed · EE-2 SIEM/Log Storage System
System Components (5): SC-1 Capability ACL Module · SC-2 Audit Event Module · SC-3 Downstream ACL Enforcement Consumer · SC-4 Downstream Audit/SIEM Forwarder · SC-5 Cargo Dependency Resolution Pipeline
Resources And Assets (2): RA-1 ACL Capability Set · RA-2 Audit Event Log Records
Technologies And Dependencies (2): TD-1 serde · TD-2 Rust non_exhaustive attribute
Use Cases (3)
- Agent Capability Authorization Check: An agent principal attempts to perform a gated action (e.g. vault read); the downstream service evaluates the agent's granted Capability set against the required capability before permitting the actio
- Security Audit Event Recording: The agent principal performs a security-relevant action; the system constructs an AuditEvent record and forwards it through the audit pipeline to log storage for compliance and incident-response purpo
- vti-common Dependency Version Adoption: A downstream platform engineering team's dependency automation resolves and merges a new vti-common patch release, incorporating any newly introduced Capability or AuditEvent variants into the build.
📋 Risk Registry (6)
| ID | Title | Severity | Residual | Priority | Effort |
|---|---|---|---|---|---|
| RISK-001 | Downstream ACL enforcement code may implement a permissive wildcard match arm on the non-exhaustive Capability enum, silently granting new capabilities. | High | High | Immediate | Medium |
| RISK-002 | Downstream audit/SIEM forwarding code may silently discard unrecognized AuditEvent variants, creating audit trail gaps. | Medium | Medium | Short-Term | Medium |
| RISK-003 | Security-relevant enum variant additions ship in patch releases, bypassing manual security review gates due to semver rules for non_exhaustive types. | Medium | Medium | Short-Term | Low |
| RISK-004 | No shared, machine-enforced contract exists across downstream consumers to guarantee consistent deny-by-default/record-default handling of unknown enum variants. | Medium | Medium | Medium-Term | Medium |
| RISK-005 | Version-skew during rolling deployments creates a window where identical requests receive inconsistent authorization outcomes across service instances. | Medium | Medium | Medium-Term | High |
| RISK-006 | Automated PR-review or LLM-based security tooling may over-weight persuasive in-code documentation narrative when assessing security-relevant diffs. | Informational | None | Long-Term | Low |
⚔️ Attack Scenarios (3)
SC-1: Capability ACL Module
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA1@{ shape: rect, label: "TA-1: Malicious Insider Downstream Developer<br><i>Introduce or exploit a permissive wildcard-arm implementation to gain silent unauthorized capability access.</i>" }
TA2@{ shape: rect, label: "TA-2: External Attacker Targeting Access Control<br><i>Exploit inconsistent capability enforcement during version-skew windows to gain unauthorized vault/memory/room access.</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S1@{ shape: rect, label: "STRIDE-1: Silent Capability Grant via Non-Exhaustive Enum Wildcard Fallthrough<br><i>High / Likely</i>" }
S8@{ shape: rect, label: "STRIDE-8: Cross-Service TOCTOU Window During Rolling Deployment<br><i>Medium / Possible</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
CAPEC122@{ shape: rect, label: "CAPEC-122: Privilege Abuse" }
CAPEC25@{ shape: rect, label: "CAPEC-25: Forced Deadlock / Race Condition" }
end
subgraph SL4["4. Weaknesses"]
direction LR
CWE863@{ shape: rect, label: "CWE-863: Incorrect Authorization" }
CWE362@{ shape: rect, label: "CWE-362: Race Condition" }
end
subgraph SL5["5. System Component"]
direction LR
SC1@{ shape: rect, label: "SC-1: Capability ACL Module" }
end
TA1 --> S1
TA2 --> S8
S1 --> CAPEC122
S8 --> CAPEC25
CAPEC122 --> CWE863
CAPEC25 --> CWE362
CWE863 --> SC1
CWE362 --> SC1
linkStyle 0 stroke:#FF0000, stroke-width:2px
linkStyle 1 stroke:#FFA500, stroke-width:2px
linkStyle 2 stroke:#FF0000, stroke-width:2px
linkStyle 3 stroke:#FFA500, stroke-width:2px
linkStyle 4 stroke:#FF0000, stroke-width:2px
linkStyle 5 stroke:#FFA500, stroke-width:2px
linkStyle 6 stroke:#FF0000, stroke-width:2px
linkStyle 7 stroke:#FFA500, stroke-width:2px
SC-2: Audit Event Module
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA4@{ shape: rect, label: "TA-4: Insider Threat Evading Audit<br><i>Perform actions mapped to new/unrecognized AuditEvent variants specifically to exploit silent-drop logging gaps and evade detection.</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S2@{ shape: rect, label: "STRIDE-2: Audit Trail Gap via Unhandled Non-Exhaustive AuditEvent Variant<br><i>Medium / Likely</i>" }
S9@{ shape: rect, label: "STRIDE-9: Audit Log Injection via Untrusted Data in AuditEvent content Field<br><i>Low / Possible</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
CAPEC268@{ shape: rect, label: "CAPEC-268: Audit Log Manipulation" }
CAPEC93@{ shape: rect, label: "CAPEC-93: Log Injection-Tampering-Forging" }
end
subgraph SL4["4. Weaknesses"]
direction LR
CWE778@{ shape: rect, label: "CWE-778: Insufficient Logging" }
CWE117@{ shape: rect, label: "CWE-117: Improper Output Neutralization for Logs" }
end
subgraph SL5["5. System Component"]
direction LR
SC2@{ shape: rect, label: "SC-2: Audit Event Module" }
end
TA4 --> S2
TA4 --> S9
S2 --> CAPEC268
S9 --> CAPEC93
CAPEC268 --> CWE778
CAPEC93 --> CWE117
CWE778 --> SC2
CWE117 --> SC2
linkStyle 0 stroke:#FFA500, stroke-width:2px
linkStyle 1 stroke:#00FF00, stroke-width:2px
linkStyle 2 stroke:#FFA500, stroke-width:2px
linkStyle 3 stroke:#00FF00, stroke-width:2px
linkStyle 4 stroke:#FFA500, stroke-width:2px
linkStyle 5 stroke:#00FF00, stroke-width:2px
linkStyle 6 stroke:#FFA500, stroke-width:2px
linkStyle 7 stroke:#00FF00, stroke-width:2px
SC-5: Cargo Dependency Resolution Pipeline
---
config:
layout: dagre
look: classic
theme: dark
---
flowchart LR
subgraph SL1["1. Threat Actors"]
direction LR
TA3@{ shape: rect, label: "TA-3: Supply Chain Manipulator<br><i>Introduce a malicious or subtly unsafe enum variant addition via a compromised maintainer account to bypass downstream review.</i>" }
end
subgraph SL2["2. Threats"]
direction LR
S3@{ shape: rect, label: "STRIDE-3: Semver-Masked Breaking Change via Non-Exhaustive Enum Extension<br><i>Medium / Likely</i>" }
S7@{ shape: rect, label: "STRIDE-7: Missing Compile-Time Enforcement of Deny-by-Default Contract<br><i>Medium / Likely</i>" }
end
subgraph SL3["3. Attack Patterns"]
direction LR
CAPEC437@{ shape: rect, label: "CAPEC-437: Supply Chain Compromise" }
CAPEC122b@{ shape: rect, label: "CAPEC-122: Privilege Abuse" }
end
subgraph SL4["4. Weaknesses"]
direction LR
CWE1104@{ shape: rect, label: "CWE-1104: Use of Unmaintained Third-Party Components" }
CWE1023@{ shape: rect, label: "CWE-1023: Incomplete Comparison with Missing Factors" }
end
subgraph SL5["5. System Component"]
direction LR
SC5@{ shape: rect, label: "SC-5: Cargo Dependency Resolution Pipeline" }
end
TA3 --> S3
TA3 --> S7
S3 --> CAPEC437
S7 --> CAPEC122b
CAPEC437 --> CWE1104
CAPEC122b --> CWE1023
CWE1104 --> SC5
CWE1023 --> SC5
linkStyle 0 stroke:#FFA500, stroke-width:2px
linkStyle 1 stroke:#FFA500, stroke-width:2px
linkStyle 2 stroke:#FFA500, stroke-width:2px
linkStyle 3 stroke:#FFA500, stroke-width:2px
linkStyle 4 stroke:#FFA500, stroke-width:2px
linkStyle 5 stroke:#FFA500, stroke-width:2px
linkStyle 6 stroke:#FFA500, stroke-width:2px
linkStyle 7 stroke:#FFA500, stroke-width:2px
📊 Risk Summary
Total Threats: 10
By Severity: Low: 3 · High: 1 · Medium: 4 · Informational: 2
By Category: Tampering: 7 · Elevation of Privilege: 5 · Repudiation: 1 · Information Disclosure: 3 · Denial of Service: 1 · Spoofing: 2
🎯 Attack Surface
Kill Chain 1: An attacker or negligent developer path begins at the Cargo Dependency Resolution Pipeline (SC-5), where a patch-level vti-common release introduces a new Capability variant permitted by #[non_exhaustive] (STRIDE-3); this flows automatically into the Downstream ACL Enforcement Consumer (SC-3) via automated dependency bots (AC-2), where a pre-existing permissive wildcard match arm (STRIDE-1, STRIDE-7) silently grants the new capability to any evaluated principal, culminating in unauthorized vault/memory/room access (RA-1) without any code change on the attacker's part. Kill Chain 2: A parallel path targets the Audit Event Module (SC-2) and Downstream Audit/SIEM Forwarder (SC-4); an insider threat actor (TA-4) times malicious actions to coincide with an unpatched consumer's silent-drop wildcard arm on a newly introduced AuditEvent variant (STRIDE-2), erasing the evidentiary trail (RA-2) and combining with potential log-injection weaknesses in unsanitized data fields (STRIDE-9) to further obscure or forge audit narratives, achieving effective repudiation of the underlying malicious action. Kill Chain 3: During coordinated rolling deployments across the Downstream Service Deployment Fleet (IF-1), an external attacker (TA-2) exploits the TOCTOU window (STRIDE-8) where mixed-version service instances hold inconsistent knowledge of newly introduced capabilities, selectively retrying requests until routed to an instance with looser enforcement, chaining this version-skew race condition with the same underlying wildcard-arm weakness (STRIDE-1) to reliably obtain unauthorized access during a narrow but predictable operational window. Together these three kill chains show that the low-level API-evolution decision to mark Capability and AuditEvent as #[non_exhaustive] — while individually low-risk and well-documented — creates a distributed trust dependency whose actual security guarantee is only as strong as the weakest downstream consumer's wildcard-arm implementation, and that dependency-update automation, rolling deployments, and audit-pipeline design all amplify this single root design decision into multiple independent exploitation windows.
🛡️ Risk Mitigation Strategy
Priority 1 (Immediate): Close the deny-by-default enforcement gap for the Capability enum by shipping an official, tested default-deny policy trait or macro directly in vti-common rather than relying on natural-language doc-comment guidance; pair this with CI contract tests that fail if any known downstream consumer implementation deviates from deny-by-default semantics, directly addressing RISK-001 and the associated STRIDE-1/STRIDE-7 threats which carry the highest severity and likelihood in this analysis. Priority 2 (Short-Term): Close the equivalent gap for AuditEvent by providing a generic fallback deserialization path that captures raw payloads for unrecognized variants, guaranteeing audit completeness independent of Rust type evolution, and extend the existing REDACTED_MARKER sanitization discipline to cover all data-carrying fields to mitigate log-injection risk (RISK-002); simultaneously, require exact-version pinning for vti-common in security-critical service manifests and introduce a security-specific changelog signal distinct from semver so that new enum variants trigger mandatory review regardless of patch-level classification (RISK-003). Priority 3 (Medium-Term): Address the ecosystem-wide consistency gap by publishing a shared conformance test suite that any downstream team can run in CI to verify their wildcard-arm implementations meet the intended deny/record-default policy (RISK-004), and invest in deployment-topology improvements — canary/blue-green rollout with capability-aware routing and feature-flagged atomic activation — to eliminate the version-skew TOCTOU window exploited during rolling deployments (RISK-005). Priority 4 (Long-Term): Strengthen review governance by mandating independent technical verification of security claims embedded in source documentation, ensuring that automated and human reviewers do not over-index on persuasive in-code narrative when assessing the actual risk of security-attribute changes (RISK-006), closing the pr
Generated by Agentic Sec — Threat Model & Affect Analysis Agent
📊 Summary & findings
| ✅ Confirmed | |
|---|---|
| 0 | 2 |
Must-Review-By-Human (2)
- 🟡 Capability enum #[non_exhaustive] enables silent allow-by-default in downstream ACL wildcard matches (triaged HIGH→MEDIUM)
- 🟡 AuditEvent enum #[non_exhaustive] enables silent audit-log gaps in downstream consumers
* feat(vti-common)!: make Capability and AuditEvent non-exhaustive Both enums are designed to grow — `Capability` gains an entry whenever the agent gains a power worth gating separately, and `AuditEvent`'s own doc comment says variants arrive alongside the features that emit them. Neither was `#[non_exhaustive]`, so every one of those additions was a breaking change for anyone matching on them. That is not hypothetical. `MemoryRead`, `MemoryWrite`, `RoomPresent`, `RoomOpen` and `AuditEvent::RoomOperation` went out in vti-common 0.16.2 — a PATCH release — so a downstream exhaustive `match` stopped compiling on a routine `cargo update`, with the caret requirement picking it up automatically. The cost inside this workspace is zero: nothing here matches exhaustively on either type. Every reference constructs a variant as a value, and the only `match self` sits in `vti-common` itself, where the attribute has no effect. Checked before writing it, not after. Downstream code now needs a `_ =>` arm, and that is the point rather than the price. A capability a consumer has never heard of is precisely the one it must not silently treat as granted, and an audit event it cannot name still has to be recorded; a wildcard arm forces both decisions to be written down instead of being decided by a compile error at the wrong moment. Deliberately NOT applied to the sixteen `vta-sdk` wire structs that broke the same way when they gained `ext`. `#[non_exhaustive]` on a struct removes literal construction from outside the crate entirely — functional update with `..Default::default()` included, which is the part people assume still works — so all sixteen would need constructors or builders. That is a redesign of the public SDK surface, not cleanup, and the safety half is now covered anyway: a new field forces a breaking bump and #1256's guard makes that stick. Worth doing deliberately, in its own change. Breaking for external consumers, so this needs the minor slot (0.17.0) rather than a patch. The guard added in #1256 will now say so if the release proposes otherwise — which makes this its first live exercise of the failure path. Signed-off-by: Glenn Gore <glenn.g@affinidi.com> * docs(releasing): say what actually counts as a breaking change Both RELEASING.md and CLAUDE.md tell an author to put `!` on "a breaking change" and then leave the term undefined, as though it were self-evident. It is not. The cases that get missed are the ones that add rather than remove, and they do not feel like breaks while you are writing them. Five went out unmarked here between 2026-08-29 and 2026-09-06: #1234 Capability::{MemoryRead, MemoryWrite} #1247 Capability::RoomPresent #1250 Capability::RoomOpen #1244 AuditEvent::RoomOperation #1231 sixteen vta-sdk wire structs gained `ext` — typed `fix:`, which derives the smallest bump there is Neither enum was `#[non_exhaustive]`, so each variant broke every downstream exhaustive `match`; the struct fields broke every literal. release-plz reads the bump off the type and the `!`, so all of it shipped as patches: vti-common 0.16.2 and vta-sdk 0.32.4, which a caret requirement picks up on a routine `cargo update`. Three of those five are mine, which is the reason to write this down rather than treat it as something careless people do. So RELEASING.md now lists the additive cases explicitly, and says the better answer is usually not the marker at all: a type designed to grow should be `#[non_exhaustive]` once (#1262), rather than depending on every future author remembering. CLAUDE.md gets the short form beside the existing title rule. Both point at the mechanical check, because neither note removes the need for one: `release bump is large enough` compares the versions a Release PR actually proposes against cargo-semver-checks and blocks when the bump is too small. It does not depend on anyone noticing. Signed-off-by: Glenn Gore <glenn.g@affinidi.com> --------- Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
Both enums are designed to grow.
Capabilitygains an entry whenever the agentgains a power worth gating separately;
AuditEvent's own doc comment saysvariants arrive alongside the features that emit them. Neither was
#[non_exhaustive], so every one of those additions was a breaking change foranyone matching on them.
Not hypothetical — it is what happened this week.
MemoryRead,MemoryWrite,RoomPresent,RoomOpenandAuditEvent::RoomOperationshipped invti-common0.16.2, a patch release, so a downstream exhaustive
matchstopped compilingon a routine
cargo update.Cost inside the workspace: zero
Nothing here matches exhaustively on either type. Every reference constructs a
variant as a value, and the only
match selfis insidevti-commonitself,where the attribute has no effect. I checked that before writing the change
rather than discovering it from a build;
cargo build/clippy --workspace --all-targets --all-featuresthen confirmed it.Why the
_ =>arm is the point, not the priceA capability a consumer has never heard of is precisely the one it must not
silently treat as granted. An audit event it cannot name still has to be
recorded. A wildcard arm forces both decisions to be written down, instead of
being made accidentally by a compile error at whatever moment the next variant
lands.
Deliberately not applied to the
vta-sdkwire structsThe sixteen structs that broke the same way when they gained
extare leftalone.
#[non_exhaustive]on a struct removes literal construction fromoutside the crate entirely — including functional update with
..Default::default(), which is the part people assume still works — so allsixteen would need constructors or builders. That is a redesign of the public SDK
surface rather than cleanup.
The safety half is covered regardless: adding a field forces a breaking bump, and
#1256's guard makes that stick. Worth doing deliberately, in its own change, with
the ergonomics thought through.
Versioning
Breaking for external consumers, so this wants the minor slot —
vti-common0.17.0, not a patch. #1256's guard will say so if the Release PR proposes
otherwise, which makes this the first live exercise of that guard's failure path;
CI has so far only seen it pass.
Note on the test suite
One full-workspace run hit
fatal runtime error: stack overflowinvta-service --test mock_vta. It passes 13/13 in isolation, and a compile-timeattribute cannot cause a runtime stack overflow, so this reads as parallel
execution pressure rather than a consequence of the change — but flagging it
rather than quietly re-running until green. Worth a look at CI's Test job.