MCP Gateway Compliance Review — 2026-05-01
Summary
Found 1 critical compliance regression introduced in commit 662a784 ("Add built-in promotion and demotion labels to GitHub guard (#4899 )" ).
The Rust WASM guard was updated to support two new allow-only policy fields, but the Go config layer that parses and forwards the policy JSON to the guard was not updated . This renders the new feature completely unusable through any supported configuration path.
Recent Changes Reviewed
Commit: 662a784265909911da7e2b622a8fdd6f7e7a2d06
Modified files:
guards/github-guard/rust-guard/src/lib.rs — added promotion_label / demotion_label serde fields to AllowOnlyPolicy
guards/github-guard/rust-guard/src/labels/helpers.rs — added promotion_label / demotion_label to PolicyContext; implemented has_promotion_label, has_demotion_label, apply_promotion_label_promotion, apply_demotion_label_demotion
guards/github-guard/rust-guard/src/labels/mod.rs — re-exports + ~20 unit tests
Critical Issues (MUST violations)
1. Go AllowOnlyPolicy struct does not include promotion-label / demotion-label
Specification Section: Guard Policy Configuration (DIFC / allow-only policy fields)
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md
Requirement:
The guard policy JSON passed from the Go gateway to the WASM guard MUST faithfully round-trip all supported policy fields. When a new field is added to the WASM guard's accepted policy schema, the Go config layer must be updated to parse and re-serialize it.
Current State:
internal/config/guard_policy.go — AllowOnlyPolicy struct has no PromotionLabel or DemotionLabel fields:
// AllowOnlyPolicy configures scope and minimum required integrity.
type AllowOnlyPolicy struct {
Repos interface {} `toml:"Repos" json:"repos"`
MinIntegrity string `toml:"MinIntegrity" json:"min-integrity"`
BlockedUsers []string `toml:"BlockedUsers" json:"blocked-users,omitempty"`
ApprovalLabels []string `toml:"ApprovalLabels" json:"approval-labels,omitempty"`
TrustedUsers []string `toml:"TrustedUsers" json:"trusted-users,omitempty"`
EndorsementReactions []string `toml:"EndorsementReactions" json:"endorsement-reactions,omitempty"`
DisapprovalReactions []string `toml:"DisapprovalReactions" json:"disapproval-reactions,omitempty"`
DisapprovalIntegrity string `toml:"DisapprovalIntegrity" json:"disapproval-integrity,omitempty"`
EndorserMinIntegrity string `toml:"EndorserMinIntegrity" json:"endorser-min-integrity,omitempty"`
// ❌ Missing: PromotionLabel / DemotionLabel
}
AllowOnlyPolicy.UnmarshalJSON (line ~178) explicitly rejects any unrecognized field:
default:
return fmt .Errorf ("allow-only contains unsupported field %q" , key )
AllowOnlyPolicy.MarshalJSON (line ~187) only serializes the known fields — promotion-label and demotion-label are never forwarded to the WASM guard.
NormalizedGuardPolicy (line ~52) likewise omits both fields.
Gap:
Any operator who tries to use the new feature:
{
"allow-only" : {
"repos" : " public" ,
"min-integrity" : " unapproved" ,
"promotion-label" : " agent-approved" ,
"demotion-label" : " agent-blocked"
}
}
via --guard-policy-json, MCP_GATEWAY_GUARD_POLICY_JSON, or a config file will receive:
allow-only contains unsupported field "promotion-label"
and the gateway will refuse to start. The feature added in 662a784 is entirely inaccessible .
Severity: Critical — feature shipped in Rust guard but is blocked at the Go config boundary.
File References:
internal/config/guard_policy.go:40-50 — AllowOnlyPolicy struct
internal/config/guard_policy.go:126-180 — AllowOnlyPolicy.UnmarshalJSON (rejects unknown fields)
internal/config/guard_policy.go:187-202 — AllowOnlyPolicy.MarshalJSON (omits new fields)
internal/config/guard_policy.go:52-64 — NormalizedGuardPolicy (missing new fields)
guards/github-guard/rust-guard/src/lib.rs:312-315 — new serde fields in Rust
Suggested Remediation Tasks
Task 1: Add PromotionLabel / DemotionLabel to Go AllowOnlyPolicy
Files: internal/config/guard_policy.go
Changes required:
Struct fields:
PromotionLabel string `toml:"PromotionLabel" json:"promotion-label,omitempty"`
DemotionLabel string `toml:"DemotionLabel" json:"demotion-label,omitempty"`
UnmarshalJSON switch cases:
case "promotion-label" :
if err := json .Unmarshal (value , & p .PromotionLabel ); err != nil {
return fmt .Errorf ("invalid allow-only.promotion-label: %w" , err )
}
case "demotion-label" :
if err := json .Unmarshal (value , & p .DemotionLabel ); err != nil {
return fmt .Errorf ("invalid allow-only.demotion-label: %w" , err )
}
MarshalJSON serialized struct:
PromotionLabel string `json:"promotion-label,omitempty"`
DemotionLabel string `json:"demotion-label,omitempty"`
NormalizedGuardPolicy struct (for observability/caching):
PromotionLabel string `json:"promotion-label,omitempty"`
DemotionLabel string `json:"demotion-label,omitempty"`
Update NormalizeGuardPolicy to populate the new fields.
Estimated Effort: Small (1–2 hours including tests)
Task 2: Add Go-side tests for new policy fields
Files: internal/config/guard_policy_test.go
Add test cases to TestNormalizeGuardPolicy (and/or a new test) covering:
Round-trip of promotion-label through ParseGuardPolicyJSON → MarshalJSON
Round-trip of demotion-label
Combination with existing fields (e.g., approval-labels + promotion-label)
Compliance Status
Section
Status
Configuration Format (Section 4.1)
✅ Compliant
Variable Expansion (Section 4.2)
✅ Compliant
Containerization (Section 3.2.1)
✅ Compliant
Protocol Behavior (Section 5)
✅ Compliant
Server Isolation (Section 6)
✅ Compliant
Authentication (Section 7)
✅ Compliant
Health Monitoring (Section 8)
✅ Compliant
Error Handling (Section 9)
✅ Compliant
Guard Policy Round-trip (DIFC)
❌ Regression in commit 662a784
References
MCP Gateway Specification
Commit reviewed: 662a784265909911da7e2b622a8fdd6f7e7a2d06
Previous clean review: 303ac98a67d930dd03448d92787b422971b13a2e (2026-04-30)
Note
🔒 Integrity filter blocked 1 item
The following item was blocked because it doesn't meet the GitHub integrity level.
get_file_contents get_file_contents: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools :
github :
min-integrity : approved # merged | approved | unapproved | none
Generated by Daily Compliance Checker · ● 895.3K · ◷
MCP Gateway Compliance Review — 2026-05-01
Summary
Found 1 critical compliance regression introduced in commit
662a784("Add built-in promotion and demotion labels to GitHub guard (#4899)").The Rust WASM guard was updated to support two new
allow-onlypolicy fields, but the Go config layer that parses and forwards the policy JSON to the guard was not updated. This renders the new feature completely unusable through any supported configuration path.Recent Changes Reviewed
662a784265909911da7e2b622a8fdd6f7e7a2d06guards/github-guard/rust-guard/src/lib.rs— addedpromotion_label/demotion_labelserde fields toAllowOnlyPolicyguards/github-guard/rust-guard/src/labels/helpers.rs— addedpromotion_label/demotion_labeltoPolicyContext; implementedhas_promotion_label,has_demotion_label,apply_promotion_label_promotion,apply_demotion_label_demotionguards/github-guard/rust-guard/src/labels/mod.rs— re-exports + ~20 unit testsCritical Issues (MUST violations)
1. Go
AllowOnlyPolicystruct does not includepromotion-label/demotion-labelSpecification Section: Guard Policy Configuration (DIFC / allow-only policy fields)
Deep Link: https://github.com/github/gh-aw/blob/main/docs/src/content/docs/reference/mcp-gateway.md
Requirement:
The guard policy JSON passed from the Go gateway to the WASM guard MUST faithfully round-trip all supported policy fields. When a new field is added to the WASM guard's accepted policy schema, the Go config layer must be updated to parse and re-serialize it.
Current State:
internal/config/guard_policy.go—AllowOnlyPolicystruct has noPromotionLabelorDemotionLabelfields:AllowOnlyPolicy.UnmarshalJSON(line ~178) explicitly rejects any unrecognized field:default: return fmt.Errorf("allow-only contains unsupported field %q", key)AllowOnlyPolicy.MarshalJSON(line ~187) only serializes the known fields —promotion-labelanddemotion-labelare never forwarded to the WASM guard.NormalizedGuardPolicy(line ~52) likewise omits both fields.Gap:
Any operator who tries to use the new feature:
{ "allow-only": { "repos": "public", "min-integrity": "unapproved", "promotion-label": "agent-approved", "demotion-label": "agent-blocked" } }via
--guard-policy-json,MCP_GATEWAY_GUARD_POLICY_JSON, or a config file will receive:and the gateway will refuse to start. The feature added in
662a784is entirely inaccessible.Severity: Critical — feature shipped in Rust guard but is blocked at the Go config boundary.
File References:
internal/config/guard_policy.go:40-50—AllowOnlyPolicystructinternal/config/guard_policy.go:126-180—AllowOnlyPolicy.UnmarshalJSON(rejects unknown fields)internal/config/guard_policy.go:187-202—AllowOnlyPolicy.MarshalJSON(omits new fields)internal/config/guard_policy.go:52-64—NormalizedGuardPolicy(missing new fields)guards/github-guard/rust-guard/src/lib.rs:312-315— new serde fields in RustSuggested Remediation Tasks
Task 1: Add
PromotionLabel/DemotionLabelto GoAllowOnlyPolicyFiles:
internal/config/guard_policy.goChanges required:
Struct fields:
UnmarshalJSONswitch cases:MarshalJSONserialized struct:NormalizedGuardPolicystruct (for observability/caching):Update
NormalizeGuardPolicyto populate the new fields.Estimated Effort: Small (1–2 hours including tests)
Task 2: Add Go-side tests for new policy fields
Files:
internal/config/guard_policy_test.goAdd test cases to
TestNormalizeGuardPolicy(and/or a new test) covering:promotion-labelthroughParseGuardPolicyJSON→MarshalJSONdemotion-labelapproval-labels+promotion-label)Compliance Status
662a784References
662a784265909911da7e2b622a8fdd6f7e7a2d06303ac98a67d930dd03448d92787b422971b13a2e(2026-04-30)Note
🔒 Integrity filter blocked 1 item
The following item was blocked because it doesn't meet the GitHub integrity level.
get_file_contents: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".To allow these resources, lower
min-integrityin your GitHub frontmatter: