Skip to content

test(phase-c): document §3 invariant 3 BoJ-side enforcement (FINDING, standards#98)#94

Closed
hyperpolymath wants to merge 1 commit into
mainfrom
feat/phase-c-boj-trust-seam-finding
Closed

test(phase-c): document §3 invariant 3 BoJ-side enforcement (FINDING, standards#98)#94
hyperpolymath wants to merge 1 commit into
mainfrom
feat/phase-c-boj-trust-seam-finding

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Phase C seam-test module for BojRest that documents the BoJ-side half
of the http-capability-gateway §3 trust-header invariant.
Tests-only — surfaces a security finding for owner decision; no
production code change in this PR.

Companion to http-capability-gateway#11 (gateway-side X-Trust-Level
strip + re-emit, opened by the dedicated hcg-tier2-channel bot earlier today).
Together they would form the Phase C defence-in-depth pair.

The finding

Phase A contract §3 invariant 3 (docs/integration/http-capability-gateway-boj-contract.md) states:

BoJ's gnosis handler / BojRest.Router MUST treat X-Trust-Level as
authoritative only when the connection originates from the gateway
... Any X-Trust-Level arriving from any other source MUST be ignored
and treated as untrusted.

BojRest.TrustPolicy.satisfies?/3 currently:

def satisfies?(_required, _trust, true), do: true
def satisfies?(:public, _trust, _local), do: true
def satisfies?(:authenticated, trust, _local) when trust in ["authenticated", "internal"], do: true
def satisfies?(_required, _trust, _local), do: false

The third clause matches regardless of is_local — so a non-loopback
caller presenting X-Trust-Level: authenticated|internal to an
:authenticated cartridge is accepted. The §3 invariant is unenforced
at the BoJ side.

Mitigation in practice today: §4 (back-side bind isolation) keeps the
non-loopback path unreachable in well-configured deployments. The §3
enforcement is nonetheless "mandatory, not advisory" per the contract.

Proposed fix (NOT in this PR)

One additional clause in satisfies?/3, between the :public clause
and the :authenticated clause:

def satisfies?(_required, _trust, false), do: false

This rejects every non-:public exposure when is_local: false, so the
non-loopback header is effectively ignored.

What this PR adds

elixir/test/phase_c_seam_test.exs186 LOC, 9 tests, 0 production code change.

Live tests (4 passing today)

  • loopback (127.0.0.1 + ::1) honours gateway-forwarded X-Trust-Level — positive control
  • non-loopback caller to a :public cartridge is still allowed (header is irrelevant) — control
  • TrustPolicy.satisfies?/3 accepts every claim when is_local: true — function-level parity

Skipped tests (5 — document the finding)

Tagged @tag skip: "Phase A §3 invariant 3 unenforced ..."; will pass
as-is when the proposed one-line fix lands. They cover:

  • non-loopback IPv4 with X-Trust-Level: internal|authenticated → expects 403
  • non-loopback IPv6 (2001:db8::1) → expects 403
  • SSE surface parity (same enforcement on /cartridge/:name/sse)
  • TrustPolicy.satisfies?/3 function-level parity for is_local: false

Test plan

  • mix test — 186 passed, 0 failed, 5 skipped (the finding)
  • live tests cover both loopback (positive) and :public-cartridge-from-non-loopback (control)
  • owner decision needed: enforce §3 at BoJ in a follow-up PR (recommended), or accept §4 as the sole defence and document the design choice
  • reviewer: confirm the skip-with-reason pattern is acceptable for a "documented finding" PR shape

Why Refs not Closes

standards#98 (Phase C) and standards#91 (HCG tier-2 channel) are
major,requirements-target — joint-close only on explicit owner
agreement. Phase C is the bot's lane; this is a complementary slice.

Refs hyperpolymath/standards#98
Refs hyperpolymath/standards#91

🤖 Generated with Claude Code

… standards#98)

Adds `elixir/test/phase_c_seam_test.exs` — a Phase C seam-test module
that complements http-capability-gateway#11 (gateway-side X-Trust-Level
strip + re-emit) by documenting the BoJ-side half of the §3
defence-in-depth pair.

## Live tests (4 passing)

* Loopback callers (127.0.0.1 + ::1) honour gateway-forwarded
  X-Trust-Level — the gateway-equivalent path.
* :public cartridge accepts a non-loopback caller regardless of header.
* `TrustPolicy.satisfies?/3` accepts every trust claim when `is_local: true`.

## Skipped tests (5 — they document a finding)

Phase A contract §3 invariant 3 states:

> Any X-Trust-Level arriving from any other source MUST be ignored
> and treated as untrusted.

`BojRest.TrustPolicy.satisfies?/3` does not currently enforce this —
its third clause (`satisfies?(:authenticated, trust, _local) when
trust in ["authenticated", "internal"]`) matches regardless of
`is_local`. A non-loopback caller reaching BoJ's back-side bind (a §4
violation) can therefore claim any trust class by setting a header.

Mitigation today: §4 (back-side bind isolation) keeps the non-loopback
path unreachable in well-configured deployments. The §3 invariant is
nonetheless "mandatory, not advisory" per the contract.

The 5 skipped tests are tagged `@tag skip: <reason>`; they will pass
as-is when the fix lands (one additional clause in `satisfies?/3`:
`def satisfies?(_required, _trust, false), do: false` between the
`:public` and `:authenticated` clauses).

Tests-only PR — production code, the bug-codifying assertions in
`trust_policy_test.exs` / `router_test.exs`, and the contract-doc
implementation note are deliberately NOT included this round, pending
owner decision on the §3 enforcement (separate follow-up PR).

`mix test` 188 → 186 + 5 skipped = same coverage, +5 skipped, +4 live;
0 failures.

Refs hyperpolymath/standards#98
Refs hyperpolymath/standards#91

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 29 issues detected

Severity Count
🔴 Critical 18
🟠 High 4
🟡 Medium 7

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Python file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/.github/scripts/validate-eclexiaiser.py",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
Copy link
Copy Markdown
Owner Author

Superseded — the test file in this PR was already squash-merged via #93 (commit a855c18 on main). This PR is a no-op duplicate. Closing.

@hyperpolymath hyperpolymath deleted the feat/phase-c-boj-trust-seam-finding branch May 20, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant