Skip to content

GITHUB_TRIPLE_LOCK: enforce integrity lock for rulesets, workflows, and primitives #34

@LalaSkye

Description

@LalaSkye

GITHUB_TRIPLE_LOCK_INVARIANT v0.1 — LOCK 2: INTEGRITY

Part of: GITHUB_TRIPLE_LOCK_INVARIANT
Related: #33 (LOCK 1: Authority)
Source: Security audit by Comet (Perplexity browser agent), 2026-03-08

Question this lock answers

Is the change set exact, bounded, and tamper-evident?

Required checks

  • Signed commits required on protected branches
  • Stale approvals dismissed on new push
  • Changed files match declared scope
  • Rulesets / decision artefacts / workflows schema-validated
  • Canonical hash or digest emitted for sensitive artefacts
  • Empty or wildcard scope cannot expand authority
  • No mutable primitive regression introduced

Audit issues addressed

Issue cluster A — StopMachine mutable / reset loop / subclass override:

  • StopMachine._state can be overwritten directly bypassing terminal RED
  • reset() allows AMBER->GREEN loops contradicting forward-only invariant
  • No subclassing guard (__init_subclass__, __slots__, final)
  • Fix: Make StopMachine @dataclass(frozen=True) with evolve() per existing spec

Issue cluster B — AuthorityGate mutable / Evidence extension / empty scope / drift bypass:

  • AuthorityGate._required can be overwritten (gate._required = Evidence.NONE)
  • Evidence(999) via IntEnum compares higher than ADMIN
  • _scope_matches with empty scope_match: {} matches every request
  • acknowledge_expansion=True bypasses drift detection
  • Fix: Freeze AuthorityGate, add __slots__, validate Evidence membership

Issue cluster C — plain dict ruleset / no hash verification:

  • evaluate() accepts any dict as ruleset with no schema validation
  • Modified rulesets with extra allowlist entries accepted silently
  • Fix: Schema-validate rulesets, emit canonical hash at load time

Issue cluster E — DecisionRecord incomplete canonical bytes:

  • canonical_bytes only hashes 5 of 10+ fields
  • actor_id, tenant_id, timestamp, gate_version, context_hash excluded
  • Two decisions with different actors produce identical hashes
  • Fix: Include all identity-bearing fields in canonical hash

Concrete actions

  1. Implement immutable StopMachine (@dataclass(frozen=True) + evolve())
  2. Freeze AuthorityGate with __slots__ + __setattr__ guard
  3. Add schema validation for ruleset JSON before evaluate() accepts it
  4. Fix _scope_matches to reject empty scope as universal match
  5. Fix DecisionRecord.canonical_bytes to include all audit-trail fields
  6. Enable "Require signed commits" in branch protection
  7. Add CI check that blocks mutable-primitive regressions on governance paths

Metadata

Metadata

Assignees

No one assigned

    Labels

    gate-reviewRequires human gate review before proceedingsecuritySecurity and access control enforcement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions