MobDev.Plugin.V1Transition (mob_dev v0.7.5)

Copy Markdown View Source

Transitional acceptance of legacy v1 plugin signature envelopes (MOB-287).

Every first-party plugin published before mob_dev shipped envelope v2 (MOB-74) is v1-signed. v1 signatures cover the evaluated manifest map, so verifying one means running Code.eval_file on priv/mob_plugin.exs first — arbitrary code execution for a malicious manifest. This module therefore evaluates a v1 manifest only after its provenance is established without evaluating anything:

  1. The plugin is a Hex dependency from the public hexpm repository: Mix resolves it through Hex.SCM right now (not a path or git dependency, including a path override pointing into deps/), the project's mix.lock pins it to hexpm, and its directory is the Hex checkout in the project's deps path. Hex package-name ownership fixes who published it; Hex verifies the tarball checksum on fetch; and the package's own mix.exs and compile step already execute during mix compile, so evaluating its manifest grants it nothing it did not already have.
  2. The public key in priv/mob_plugin.pub has the fingerprint trusted for that plugin name in config :mob, :trusted_plugins.

Only then is the manifest evaluated and the v1 signature checked against it (3). Any failure reports :envelope_v1_unsupported, the same refusal every other v1 envelope gets.

Delete this module — and its two call sites in Verify.load_verified/2 and SignatureGate — once every first-party plugin is republished with a v2 signature. See decisions/2026-09-30-v1-envelope-transition.md.

Summary

Types

Provenance inputs. Each defaults to the current Mix project's value

Functions

Whether an already-loaded manifest for a v1-signed plugin satisfies the transition rule. Never evaluates anything — used by SignatureGate, which receives manifests MobDev.Plugin.activated/0 already loaded.

Loads the manifest of a v1-signed plugin when the transition rule allows it.

The one-line build notice for an accepted v1 plugin.

Types

opts()

@type opts() :: [
  scms: %{optional(atom()) => module()},
  lock: map(),
  deps_path: Path.t(),
  trust_map: MobDev.Plugin.TrustStore.trust_map()
]

Provenance inputs. Each defaults to the current Mix project's value:

  • :scms — each dependency's active SCM (Mix.Project.deps_scms/0).
  • :lock — the parsed mix.lock map (Mix.Dep.Lock.read/0).
  • :deps_path — the project's deps directory (Mix.Project.deps_path/0).
  • :trust_map — config :mob, :trusted_plugins (TrustStore.load_trusted_plugins/0).

Functions

accepted?(plugin_dir, manifest, opts \\ [])

@spec accepted?(Path.t(), map() | nil, opts()) :: boolean()

Whether an already-loaded manifest for a v1-signed plugin satisfies the transition rule. Never evaluates anything — used by SignatureGate, which receives manifests MobDev.Plugin.activated/0 already loaded.

load(plugin_dir, opts \\ [])

@spec load(Path.t(), opts()) :: {:ok, map()} | {:error, :envelope_v1_unsupported}

Loads the manifest of a v1-signed plugin when the transition rule allows it.

The manifest is evaluated only after the plugin passes the Hex-provenance and trusted-fingerprint checks; a plugin failing either is refused without evaluation. Returns {:error, :envelope_v1_unsupported} on any failure.

notice(plugin_dir, opts \\ [])

@spec notice(Path.t(), opts()) :: String.t()

The one-line build notice for an accepted v1 plugin.