Skip to content

[compliance] Compliance Gap: Go config layer missing promotion-label/demotion-label fields added to Rust WASM guard (commit 662a784) #4920

Description

@github-actions

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.goAllowOnlyPolicy 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-50AllowOnlyPolicy struct
  • internal/config/guard_policy.go:126-180AllowOnlyPolicy.UnmarshalJSON (rejects unknown fields)
  • internal/config/guard_policy.go:187-202AllowOnlyPolicy.MarshalJSON (omits new fields)
  • internal/config/guard_policy.go:52-64NormalizedGuardPolicy (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:

  1. Struct fields:

    PromotionLabel string `toml:"PromotionLabel" json:"promotion-label,omitempty"`
    DemotionLabel  string `toml:"DemotionLabel"  json:"demotion-label,omitempty"`
  2. 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)
        }
  3. MarshalJSON serialized struct:

    PromotionLabel string `json:"promotion-label,omitempty"`
    DemotionLabel  string `json:"demotion-label,omitempty"`
  4. NormalizedGuardPolicy struct (for observability/caching):

    PromotionLabel string `json:"promotion-label,omitempty"`
    DemotionLabel  string `json:"demotion-label,omitempty"`
  5. 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 ParseGuardPolicyJSONMarshalJSON
  • 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 ·

Metadata

Metadata

Assignees

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