Relyra.Security.XML.CorpusGate (relyra v1.1.0)

Copy Markdown View Source

Runtime security-corpus gate for the Phase 21 scheduled-refresh path per D-21.

Every existing security-corpus fixture acts as a refusal trigger on the scheduled path. If freshly-fetched metadata trips a known-bad shape (xml-crypto 2025 signature-wrapping family, PortSwigger Fragile-Lock namespace-confusion shapes, etc.), this gate refuses the apply with {:error, %Relyra.Error{type: :corpus_violation}} so the wrapper sets auto_suspended_reason: :corpus_violation per the LOCKED enum.

The corpus manifest physically lives at priv/security_corpus.json so both this runtime gate AND the test corpus reader at test/security/xml/corpus_security_test.exs can read the same source of truth without crossing the lib/test boundary in either direction.

Detection model

The gate evaluates the candidate XML against byte-level refusal shapes derived from each manifest fixture's expected_error_type. The pre-parse refusal shapes (:doctype_forbidden, :entity_expansion_forbidden, :payload_too_large) catch the trust-boundary attacks the gate is responsible for at the metadata-XML byte level — these are the same pre-parse early-rejection conditions the hardened parser (Relyra.Security.XML.PureBeam) already enforces, but routed through the Phase-21 :corpus_violation typed error so the wrapper can set the matching auto_suspended_reason.

Pure: no Repo, no Req, no telemetry. Reads priv/security_corpus.json once at compile time (a @manifest module attribute).

Summary

Functions

Checks freshly-fetched metadata XML against the LOCKED security-corpus regression fixtures. Returns :ok if no fixture's refusal-shape matches; {:error, %Relyra.Error{type: :corpus_violation, details: %{matched_fixture_id: id, class: class, expected_error_type: type}}} if any does.

Returns the loaded manifest fixtures (one map per fixture).

Returns the path the gate reads the manifest from at compile time.

Functions

check(xml, opts \\ [])

@spec check(
  binary(),
  keyword()
) :: :ok | {:error, Relyra.Error.t()}

Checks freshly-fetched metadata XML against the LOCKED security-corpus regression fixtures. Returns :ok if no fixture's refusal-shape matches; {:error, %Relyra.Error{type: :corpus_violation, details: %{matched_fixture_id: id, class: class, expected_error_type: type}}} if any does.

xml is the raw bytes; opts accepts :max_bytes to override the default 5 MB cap that mirrors the Phase-21 stricter Req profile (D-20).

manifest()

@spec manifest() :: [map()]

Returns the loaded manifest fixtures (one map per fixture).

manifest_path()

@spec manifest_path() :: String.t()

Returns the path the gate reads the manifest from at compile time.