\* Domain: r ∈ AccessRequest, c ∈ ToolConfig, Decision(r,c) ∈ {allow, deny(code)}
\* Source: README §Formal Model, spec §4.5.3 (evaluation order)
ALLOW(r, c) ≜
P1_ToolAllowed(r, c) ∧
P2_RepoMatch(r, c) ∧
P3_RoleAllow(r, c) ∧
P4_PrivateRepoAllow(r, c) ∧
P5_NotBlocked(r, c) ∧
P6_IntegrityMet(r, c)
\* P1 — "the requested tool name must be present [in allowed-tools]; an empty or
\* absent tool name against a non-empty list also denies"
P1_ToolAllowed(r, c) ≜
c.AllowedTools = {} ∨ r.ToolName ∈ c.AllowedTools
\* P2 — "if repos is configured, repository matches at least one pattern
\* (owner/repo, owner/*, */repo, */*); omitted repos allows all; empty array
\* is a compile-time validation error, runtime treats as no-match"
P2_RepoMatch(r, c) ≜
c.Repos = NIL
∨ ∃ p ∈ c.Repos : MatchesPattern(p, r.Repository)
\* P3 — "if roles is configured, user role matches one configured role (OR-logic)"
P3_RoleAllow(r, c) ≜
c.Roles = {} ∨ r.UserRole ∈ c.Roles
\* P4 — "private repository access is denied when private-repos: false"
P4_PrivateRepoAllow(r, c) ≜
¬(c.PrivateRepos = FALSE ∧ r.IsPrivate = TRUE)
\* P5 — "blocked users are denied within integrity management (author check)",
\* evaluated BEFORE P6 in the integrity-management sub-phase
P5_NotBlocked(r, c) ≜
r.UserLogin ∉ c.BlockedUsers
\* P6 — "integrity ordering is enforced as none < unapproved < approved < merged";
\* an unrecognized MinIntegrity config is fail-safe (denies all requests);
\* an unknown ContentIntegrity has rank -1, below any valid threshold
Rank(level) ≜ CASE level = "none" -> 0
[] level = "unapproved" -> 1
[] level = "approved" -> 2
[] level = "merged" -> 3
[] OTHER -> -1
P6_IntegrityMet(r, c) ≜
c.MinIntegrity = "" ∨
(Rank(c.MinIntegrity) ≥ 0 ∧ Rank(r.ContentIntegrity) ≥ Rank(c.MinIntegrity))
\* INV1 — combined-allow invariant: ALLOW holds iff every guard holds jointly
INV1_CombinedAllow(r, c) ≜ ALLOW(r, c) ⟺ (P1 ∧ P2 ∧ P3 ∧ P4 ∧ P5 ∧ P6)(r, c)
\* INV2 — the returned deny error code equals the code of the FIRST guard (in
\* documented order 1..6) that evaluates to false
INV2_ErrorCode(r, c) ≜
¬ALLOW(r, c) ⟹ ErrorCode(r, c) = FirstFailingGuardCode(r, c)
\* SAFETY_BlockedUserAlwaysDenied — a blocked user is always denied (-32005),
\* even when tool/repo/role/private guards all pass
SAFETY_BlockedUserAlwaysDenied(r, c) ≜
(P1 ∧ P2 ∧ P3 ∧ P4)(r, c) ∧ r.UserLogin ∈ c.BlockedUsers
⟹ ErrorCode(r, c) = -32005
\* SAFETY_NoSpuriousAllow — no allow decision is produced when any single guard
\* fails; i.e. ALLOW is monotone-conjunctive with no bypass path
SAFETY_NoSpuriousAllow(r, c) ≜
(¬P1 ∨ ¬P2 ∨ ¬P3 ∨ ¬P4 ∨ ¬P5 ∨ ¬P6)(r, c) ⟹ ¬ALLOW(r, c)
Warning
threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.
Details
The threat detection engine failed to produce results.
Review the workflow run logs for details.
Summary
specs/github-mcp-access-control-compliance/README.mddocuments the Section 11 compliancefixtures for the GitHub MCP Access Control Specification. The runtime access decision is
modeled as a conjunction of six ordered guard predicates (
P1–P6) evaluated over anAccessRequestand aToolConfig, producingallowordeny(code). This run formalizesthe six guards plus two safety invariants using TLA+/Z3-style illustrative notation and
verifies the mapping against the existing executable Go conformance suite
(
pkg/workflow/github_mcp_access_control_formal_test.go), which also drives all ten YAMLfixtures in the spec directory through the same evaluator.
Specification
specs/github-mcp-access-control-compliance/README.mdFormal Model
Predicates and invariants (illustrative notation)
Behavioral Coverage Map
P1_ToolAllowedTestFormal_ToolNameFilterallowed-toolsallows named tool, denies others; empty tool name denies against non-empty listP2_RepoMatch(exact)TestFormal_ExactMatchAllowowner/repopattern allows matching repo, denies othersP2_RepoMatch(wildcard)TestFormal_WildcardMatchowner/*,*/repo,*/*wildcard formsP2_RepoMatch(omitted/empty)TestFormal_OmittedReposAllowAllreposallows all; empty array (invalid config) denies at runtimeP3_RoleAllowTestFormal_RoleFilter-32003P4_PrivateRepoAllowTestFormal_PrivateRepoControlprivate-repos: falseblocks private repos with-32004; public unaffectedP5_NotBlockedTestFormal_BlockedUserDeny-32005P6_IntegrityMet(ordering)TestFormal_IntegrityLevelOrdernone<unapproved<approved<mergedenforcedP6_IntegrityMet(unknown content — edge case)TestFormal_UnknownContentIntegrityDeniedContentIntegrity(rank -1) denied below any thresholdP6_IntegrityMet(invalid config — edge case)TestFormal_InvalidMinIntegrityConfigDeniedMinIntegrityis fail-safe: denies allINV1_CombinedAllowTestFormal_CombinedFiltersAllAllowINV2_ErrorCode(edge case: multi-failure)TestFormal_ErrorCodeFirstFailingGuardSAFETY_BlockedUserAlwaysDeniedTestFormal_BlockedUserSafetyProperty-32005when earlier guards passSAFETY_NoSpuriousAllowTestFormal_NoSpuriousAllowInvariantP5 before P6(edge case)TestFormal_FixtureRunnerGenerated Test Suite
📄 Verification note
An executable formal-model conformance suite for this specification already exists in the
repository at
pkg/workflow/github_mcp_access_control_formal_test.go(495 lines). Itimplements every predicate above via a self-contained
formalEvaluateAccessevaluator(no stubs required — the config/request types and pattern-matching, role, private-repo,
blocked-user, and integrity-rank logic are all inlined in the test file) and additionally
includes a
TestFormal_FixtureRunnerthat loads all 10 YAML fixtures fromspecs/github-mcp-access-control-compliance/*.yamland drives them through the sameevaluator, binding the fixtures to the formal model directly. This run's formalization
(above) was cross-checked against that implementation predicate-by-predicate; no gaps were
found — the existing suite already satisfies the required coverage (P1–P6, INV1, INV2,
both safety properties, and 3+ edge cases: empty-repos, unknown-integrity-value,
invalid-min-integrity-config).
Key evaluator excerpt (guard order, from the existing file):
Usage
pkg/workflow/github_mcp_access_control_formal_test.go— no copy needed.go test -v -run "TestFormal_" ./pkg/workflow/go test -v -run "TestFormal_FixtureRunner" ./pkg/workflow/Context
specs/github-mcp-access-control-compliance/README.mdINV1,INV2, 2 safety properties (12 total)