Skip to content

openshell: Layer 1 strict-mode should skip rules with unavailable PolicyContext fields #885

@IngmarVG-IB

Description

@IngmarVG-IB

Background

Spun off from PR #469 review (round 2). The Guard's Check method evaluates published policy documents as Layer 1 (caller-side, pre-connection). At that layer, PolicyContext can only be populated with CallerID, CallerDomain, and Protocol. Connection-state fields — AuthType, HasMutualTLS, TLSVersion, DNSSECValidated, CallerTrustScore, Method, Intent, GeoCountry, ConsentToken — cannot be filled in until the connection actually exists.

In ModePermissive (the default) this is fine: rules dependent on those fields produce warnings only.

In ModeStrict, however, any policy that publishes required_auth_types, require_dnssec, require_mutual_tls, min_tls_version, required_caller_trust_score, allowed_methods, allowed_intents, geo_restrictions, or consent_required will unconditionally deny because the zero value never satisfies the rule.

PR #469 documents this prominently in the Guard doc comment but stops short of the code fix.

Proposal

In pkg/openshell/evaluator.go::Evaluate, when a rule applies at the current layer but the corresponding PolicyContext field is at its zero value (i.e., we genuinely don't know the answer yet), emit a warning instead of a violation. Concretely:

  • required_auth_types with pctx.AuthType == \"\" → warning (not violation)
  • require_dnssec with !pctx.DNSSECValidated AND layer == LayerCaller → warning
  • require_mutual_tls with !pctx.HasMutualTLS AND layer == LayerCaller → warning
  • min_tls_version with empty TLSVersion → warning
  • required_caller_trust_score with nil score → warning
  • allowed_methods / allowed_intents with empty value → warning
  • geo_restrictions with empty GeoCountry → warning
  • consent_required with empty ConsentToken AND layer == LayerCaller → warning

The signal flips back to violation when (a) we're at Layer 2 with the value populated, or (b) the field is set at Layer 1 and the comparison fails for real.

Acceptance criteria

  • ModeStrict at Layer 1 against a policy that requires DNSSEC + mutual TLS no longer denies a connection that hasn't been initiated yet
  • ModeStrict at Layer 2 against the same policy still denies if the connection comes back without DNSSEC / mTLS
  • Doc-comment limitation note on Guard can be downgraded or removed
  • Table-driven test covering each rule's "unavailable field at Layer 1" path

Out of scope

  • Cap-doc fetch / DNSSEC/DANE validation / Phase 6 policy enforcement (separate roadmap items in the integration README)

Context

PR #469 review thread (round 2 follow-up by @mchmarny): #469 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    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