Skip to content

feat: add configurable trusted bots list with approved integrity elevation#2204

Merged
lpcox merged 1 commit intomainfrom
copilot/add-trusted-bots-support
Mar 20, 2026
Merged

feat: add configurable trusted bots list with approved integrity elevation#2204
lpcox merged 1 commit intomainfrom
copilot/add-trusted-bots-support

Conversation

Copy link
Contributor

Copilot AI commented Mar 20, 2026

The mcp-gateway schema defines a trustedBots gateway config field for operator-specified bots that should receive approved integrity (same as built-in bots like dependabot[bot], github-actions[bot]). This wires that field end-to-end from config parsing through the WASM guard.

Config

  • GatewayConfig.TrustedBots []string — TOML: trusted_bots
  • StdinGatewayConfig.TrustedBots []string — JSON stdin: trustedBots
[gateway]
trusted_bots = ["copilot-swe-agent[bot]", "my-org-bot[bot]"]

Guard payload (internal/guard/wasm.go)

  • BuildLabelAgentPayload(policy, trustedBots) — merges bots into the label_agent JSON payload as trusted-bots
  • buildStrictLabelAgentPayload — relaxed to accept trusted-bots as an optional top-level key alongside allow-only; all other unexpected keys still rejected

Server (internal/server/unified.go)

  • ensureGuardInitialized builds the combined payload before calling LabelAgent; policyHash covers both policy and trusted bots, so session cache invalidates on any change

Rust guard

  • LabelAgentInput.trusted_bots — deserialized from trusted-bots (default [])
  • PolicyContext.trusted_bots: Vec<String> — populated at label_agent parse time, available to all label functions via the existing context pattern
  • is_configured_trusted_bot(username, ctx) — case-insensitive membership check
  • author_association_floor() — now checks both is_trusted_first_party_bot and is_configured_trusted_bot; either match elevates to writer_integrity (approved)

The list is purely additive — it cannot remove built-in trusted bots.

Implements the `trustedBots` field from the new mcp-gateway config schema.
Bots listed here receive approved integrity for objects they author, additive
to the built-in trusted bot list (dependabot[bot], github-actions[bot], etc.).

- GatewayConfig.TrustedBots []string (TOML: trusted_bots)
- StdinGatewayConfig.TrustedBots []string (JSON stdin: trustedBots)
- guard.BuildLabelAgentPayload() merges trusted bots into label_agent payload
- buildStrictLabelAgentPayload() validates optional trusted-bots key
- UnifiedServer.getTrustedBots() + updated ensureGuardInitialized()
- Rust: LabelAgentInput.trusted_bots, PolicyContext.trusted_bots,
  is_configured_trusted_bot(), author_association_floor() checks both lists
- Tests for all new Go and Rust functionality

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/2adba402-27f1-4075-b747-df6840536210
@lpcox lpcox marked this pull request as ready for review March 20, 2026 03:57
Copilot AI review requested due to automatic review settings March 20, 2026 03:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a gateway-level configuration hook for operator-specified “trusted bots” and wires it end-to-end into the WASM GitHub guard so those actors receive approved (writer) integrity (matching existing built-in trusted bots).

Changes:

  • Introduces GatewayConfig.TrustedBots (trusted_bots in TOML) and StdinGatewayConfig.TrustedBots (trustedBots in stdin JSON).
  • Extends the label_agent transport payload to optionally include trusted-bots, and updates strict payload validation + Go unit tests.
  • Extends the Rust guard’s PolicyContext and integrity-floor logic to honor configured trusted bots (case-insensitive), with new tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/server/unified.go Builds a combined label_agent payload (policy + trusted bots), hashes it for session cache invalidation, and passes it to LabelAgent.
internal/guard/wasm.go Allows trusted-bots as an optional top-level key in strict payload validation; adds helper to build the merged payload.
internal/guard/wasm_test.go Adds coverage for strict payload acceptance/rejection of trusted-bots and for BuildLabelAgentPayload.
internal/config/config_stdin.go Adds stdin JSON support for gateway.trustedBots and converts it into internal config.
internal/config/config_core.go Adds TOML/JSON struct field GatewayConfig.TrustedBots with documentation.
guards/github-guard/rust-guard/src/lib.rs Deserializes trusted-bots from label_agent input and stores it in PolicyContext.
guards/github-guard/rust-guard/src/labels/mod.rs Updates/extends tests and adds new tests validating configured-trusted-bot behavior.
guards/github-guard/rust-guard/src/labels/helpers.rs Adds PolicyContext.trusted_bots, implements membership check, and integrates it into author_association_floor.
Comments suppressed due to low confidence (1)

internal/server/unified.go:1173

  • trustedBots is taken directly from config and injected into the label_agent payload/hash without normalization. Duplicate entries, leading/trailing whitespace, casing differences, or reordered lists will unnecessarily invalidate the cached session state (policyHash) and can also cause label_agent to fail later if any entry is blank/whitespace-only. Consider canonicalizing once (trim + drop empty + dedupe, and optionally sort / lowercase) before building the payload and computing policyHash (e.g., via internal/strutil.DeduplicateStrings).
	// Build the label_agent payload, merging in any configured trusted bots.
	// The policyHash covers both the policy and trusted bots so that any change
	// to either field invalidates the cached guard session state.
	trustedBots := us.getTrustedBots()
	labelAgentPayload := guard.BuildLabelAgentPayload(policy, trustedBots)
	payloadJSON, err := json.Marshal(labelAgentPayload)
	if err != nil {
		return defaultMode, fmt.Errorf("failed to serialize label_agent payload: %w", err)
	}
	policyHash := string(payloadJSON)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -1175,7 +1185,7 @@ func (us *UnifiedServer) ensureGuardInitialized(

log.Printf("[DIFC] Initializing guard session state: server=%s, session=%s, policy_source=%s", serverID, sessionID, source)
log.Printf("[DIFC] Calling label_agent: server=%s, session=%s, guard=%s, policy=%s", serverID, sessionID, g.Name(), string(policyJSON))
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log line for "Calling label_agent" prints policyJSON, but the actual payload passed to LabelAgent is labelAgentPayload, which may include the trusted-bots key. This makes debugging policy/session-cache issues harder because logs won’t reflect what was sent to the guard. Consider logging payloadJSON (or renaming the field in the log to "payload") when trusted bots are configured.

This issue also appears on line 1164 of the same file.

Suggested change
log.Printf("[DIFC] Calling label_agent: server=%s, session=%s, guard=%s, policy=%s", serverID, sessionID, g.Name(), string(policyJSON))
log.Printf("[DIFC] Calling label_agent: server=%s, session=%s, guard=%s, payload=%s", serverID, sessionID, g.Name(), string(payloadJSON))

Copilot uses AI. Check for mistakes.
Comment on lines 282 to +286
cfg.Gateway.PayloadDir = stdinCfg.Gateway.PayloadDir
}
if len(stdinCfg.Gateway.TrustedBots) > 0 {
cfg.Gateway.TrustedBots = stdinCfg.Gateway.TrustedBots
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convertStdinConfig copies trustedBots into cfg.Gateway.TrustedBots without any validation/normalization. If stdin contains entries like "" or " ", guard initialization will fail later in buildStrictLabelAgentPayload with a less actionable error at runtime. Consider validating each entry here (trim + require non-empty) and normalizing (dedupe/sort) so config errors fail fast during load.

Copilot uses AI. Check for mistakes.
Comment on lines +902 to +903
let lower = username.to_lowercase();
ctx.trusted_bots.iter().any(|b| b.to_lowercase() == lower)
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_configured_trusted_bot allocates on every call (username.to_lowercase() and b.to_lowercase() for each entry). This function is used in the integrity floor path and can run frequently, so this may become a hot spot. Consider avoiding allocations by using eq_ignore_ascii_case for comparisons and/or storing a pre-normalized (lowercased) set/vector in PolicyContext at label_agent parse time.

Suggested change
let lower = username.to_lowercase();
ctx.trusted_bots.iter().any(|b| b.to_lowercase() == lower)
ctx.trusted_bots
.iter()
.any(|b| b.eq_ignore_ascii_case(username))

Copilot uses AI. Check for mistakes.
@lpcox lpcox merged commit 2e4bc62 into main Mar 20, 2026
7 checks passed
@lpcox lpcox deleted the copilot/add-trusted-bots-support branch March 20, 2026 04:19
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.

3 participants