Skip to content

dpp-plugin-host: Wasm sandbox limiter gap; spoofable sandbox alerts; missing in-force gate on one output path #17

Description

@LKSNDRTMLKV

Static read-through audit of crates/dpp-plugin-host, the Wasm plugin sandbox. Guest pointer/length pairs are properly bounds-checked and size-capped; no unsafe in this crate. Four verified findings.

1. Table growth isn't metered, unlike linear memory

Location: src/runtime.rs:63-70
Issue: ResourceLimiter::table_growing always returns Ok(true), in contrast to memory_growing, which enforces memory_cap_bytes. The doc comment claims linear memory up to DEFAULT_MEMORY_CAP_BYTES is "the enforced bound."
Failure scenario: A plugin can issue a single table.grow with a huge element count to force a large host-side allocation, metered by fuel only as one instruction — not by the amount grown. A real resource-limit bypass distinct from the (properly capped) linear-memory path.

2. Sandbox-event metrics are spoofable by plugin-controlled text

Location: src/host.rs:142-168
Issue: Fuel-exhaustion and memory-cap detection classify the error via msg.to_lowercase().contains("fuel") / contains("memory cap") on e.to_string(), which partly derives from plugin-controlled PluginError text (Internal(String), etc., all fields are guest-controlled).
Failure scenario: A plugin returning PluginError::Internal("fuel".into()) triggers the plugin_fuel_exhausted_total metric/alert without ever exhausting fuel — directly contradicting the code's own comment that this alert is "unambiguous."

3. Provisional/in-force compliance gate is bypassable on one output path

Location: src/host.rs:53-97 (generate_passport_payload) vs 184-185 (compute)
Issue: PluginHost::compute centrally enforces gate_determination(catalog().is_in_force(key), ...) so a provisional sector can never surface a binding compliance status. generate_passport_payload has no equivalent — it only injects an advisory __isInForce flag into the input and trusts the plugin to self-limit.
Failure scenario: A plugin can ignore that flag and emit a fully "compliant"-looking passport for a sector that isn't legally in force, with no host-side backstop on this path.

4. Unsigned-plugin loading fails open with no environment guard

Location: src/loader/plugin.rs:42-64, src/loader/signing.rs
Issue: When trusted_key is None, loading proceeds with only a tracing::warn!. The doc comment says "Production deployments must always provide a key," but nothing enforces that.
Failure scenario: A misconfigured deploy silently runs unsigned/unverified plugins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-relevant issue

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions