Skip to main content

Crate pkix_policy_zlint

Crate pkix_policy_zlint 

Source
Expand description

Thin pkix_lint::Lint adapter over pkix_zlint_bridge::ZlintBridge.

pkix-policy-zlint exposes each of zlint’s per-check verdicts as a workspace pkix_lint::Lint implementation, so callers can mix zlint findings into a pkix_lint::LintRunner alongside the workspace’s own RFC-conformance and -cabf reference lints, without any awareness that the verdicts come from a subprocess.

Per AGENTS.md non-negotiable #5 (three-mode policy-class model), this is the principled path for predicate-comprehensive CA/B Forum coverage. zlint stays the source of truth for the checks it covers; the workspace contributes the framework, not the policy. The hand- authored pkix-lint-cabf reference set covers a small curated subset of marquee BR predicates; this crate covers the whole catalog (~400 lints at the time of writing).

§Usage

use std::sync::Arc;
use pkix_lint::LintRunner;
use pkix_zlint_bridge::{BridgeConfig, ZlintBridge};
use pkix_policy_zlint::all_lints;

// Construct one bridge per process. The bridge owns the per-cert
// verdict cache that amortises subprocess cost across all ~400
// wrapped lints.
let bridge = Arc::new(ZlintBridge::new(BridgeConfig::default())?);

// Enumerate zlint's catalog once and wrap each check as a Lint.
let lints = all_lints(bridge)?;

// Hand to the workspace LintRunner like any other Lint set.
let runner = LintRunner::new(lints);

§Lint identity is leaked at adapter-construction time

pkix_lint::Lint::id and pkix_lint::Lint::citation return &'static str. zlint’s catalog is enumerated at runtime, so the metadata strings (check_id, citation, description) are Strings owned by pkix_zlint_bridge::ZlintLintInfo. To satisfy the &'static str contract, all_lints leaks each ZlintLintInfo once at construction time via Box::leak, and the resulting ZlintLint holds a &'static ZlintLintInfo.

The leak is bounded and intentional: ~400 leaks of small ZlintLintInfo records (~150 bytes each, dominated by the catalog’s description string) at startup, never growing. This trades a small one-time program-lifetime allocation for &'static str-shaped metadata methods on the workspace pkix_lint::Lint trait. The alternative — broadening the trait to Cow<'static, str> or Box<str> — was rejected because it would ripple through every hand-authored Lint impl in pkix-lint, pkix-lint-cabf, and external crates for the sake of a single adapter.

§Mapping zlint verdicts to pkix_lint::LintResult

pkix_zlint_bridge::Verdictpkix_lint::LintResult
NotApplicableNotApplicable
PassPass
NoticeWarn(detail)
WarnWarn(detail)
ErrorError(detail)
FatalFatal(detail)

Notice collapses into Warn at the LintResult level because LintResult does not have a separate notice variant — the workspace Severity::Notice is metadata on the lint, not on the per-cert result. Reports that need to differentiate Notice from Warn consult pkix_lint::Lint::severity (which forwards from ZlintLintInfo::severity).

Per-cert bridge errors (malformed DER, zlint runtime panic for that cert) surface as LintResult::Error with the underlying error’s Display text as detail.

§Limitations

  • One ZlintBridge per program. The bridge owns the per-certificate verdict cache; if you construct multiple bridges you pay the subprocess cost multiple times. Wrap the bridge in Arc and share it across all ZlintLint instances (which is what all_lints does).
  • SubjectKind::Any for every wrapped lint. zlint’s catalog metadata does not surface a stable “applies-to” classification (leaf vs. intermediate vs. anchor); zlint itself internally determines applicability and returns the per-cert Verdict::NotApplicable when the rule does not fire. The adapter therefore reports SubjectKind::Any and lets zlint’s own logic do the filtering.
  • Scope::Certificate only. zlint operates on a single certificate at a time; the adapter mirrors that. Path-scope lints (chain-wide rules like algorithm consistency across intermediates) live in pkix-lint and pkix-lint-cabf, not here.
  • Per-check parameters() empty. zlint exposes no externally-configurable parameters per check; the adapter does not invent a parameter surface that zlint does not have.
  • title() and description() use the leaked ZlintLintInfo’s fields verbatim. They are not localised, paraphrased, or reformatted — what zlint says is what callers see.

Structs§

ZlintLint
One workspace Lint wrapping one zlint check.

Functions§

all_lints
Enumerate zlint’s catalog via bridge and wrap every check as a workspace Lint.