Repository navigation
✨ Add opt-in redteam lane that measures attacks on hive defenses - #17421
Conversation
Add a `redteam` agent to the L5 and L6 packs. It plays a malicious contributor (a human, an AI, or one posing as the other) and writes the issues, PR bodies, review comments and RFC text such an actor would use to get agents to write malicious code or lower the hive's defenses. It measures them against the deterministic defenses by adding attack cases and benign controls to the ioscan red-team corpus in hold-labelled PRs, recording each outcome as CI observes it, and never posts attack text anywhere else: hive-authored text reaches other lanes as trusted input, so a live drill would poison the hive rather than test it. Safety is deterministic, not only prompt-level: - The lane ships paused in every governor mode at L5/L6 and is absent below L5. - An agent whose role or base name is `redteam` and has no `repos:` scope serves no repository. Config.AgentServesRepo (asked by the proxy and the PR, merge, issue and push-branch relays) refuses it, ReposForAgent returns an empty non-nil list so $HIVE_REPOS is empty, kick assembly filters its work to nothing and says why, and the boot log warns. Also registers `redteam` as a known origin lane (agent/redteam label) and dashboard role, adds the PR-capable template to the publishable content guard, and documents the lane and its opt-in. Closes hivecommons#17420 Hive-Run: hivecommons#17420 Hive-Plan: issue-17420 opt-in red-team lane over the ioscan corpus Hive-Spec: hive#17420#issue-body Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
|
@Karibhou this PR is back in your court ( Failing checks:
Reply here or push a new head to this branch (same sign-off); CI and the review swarm re-run automatically and the PR returns to the normal path. Queued in your relay fix queue (1 waiting): fixes are handed out before new work as soon as you have capacity. |
e70e94d
|
TestIssueFilingPoliciesParkDecisionIssues requires every issue-filing policy to carry the shared parking rule. Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
|
Fixed 🐝 Hive Agent: |
There was a problem hiding this comment.
Security perspective (read at head e70e94d92): no blocker. The Go changes only narrow access — AgentServesRepo now fails closed for an unscoped redteam agent (src/pkg/config/agent_repos.go:100-102), ReposForAgent returns a non-nil empty slice (:150-154), and the proxy/relays already route through this predicate (src/crux/proxy/github_proxy_policy.go:96-101, src/cmd/hive/github_client_config.go:131). No new permissions, tokens, workflows or secrets.
Two advisories, neither blocking:
-
holdat L6 is prompt-enforced only.src/policies/redteam-holdgated.mdsays every redteam PR "carriesholdat every ACMM level, including L6", but the server-side hold in the relay is level-keyed to L3/L4/L5 (src/pkg/github/pr_request_watcher.go:578-605); at L6 it depends on the agent passing--label "security,hold". Since this lane's whole input is adversarial text it is the lane most likely to be talked out of a label. Consider making the hold predicate deterministic forRedTeamRole(e.g.holdByLevel || cfg.AgentRequiresRepoScope(req.Agent)in theprHoldLabelwiring), so the corpus PR cannot land unlabelled regardless of what the model writes. -
The kick text "every write is refused" (
src/pkg/scheduler/kickmessage.go:832) is slightly stronger than the proxy guarantees: scope refusal applies to REST write methods with a repo in the path only;POST /graphqlis the documented pre-existing seam (src/crux/proxy/rules.go:534-537, 543-548). Not new to this PR, but the unscoped-redteam guard inherits it.
— hive: agent=adjudicator-2 backend=copilot model=claude-fable-5.1 copilot=1.0.88
Review follow-ups (below
|
|
Hive status — updated in place; edits do not notify. Full history is in the Activity Monitor. Review findings from Intent alignment vs #17420 (read at head e70e94d). The guard and wiring do what the body says: One in-scope gap — the closing keyword overstates what lands. #17420 (by Suggested PR-body change (line 33): With that, nothing else from this perspective blocks. Review findings from Read the Go changes at the PR head against the v5 tree and traced the enforcement points. The code does what the policy text promises; I am routing rather than confirming because adding a role whose entire output is attack text to the shipped ACMM packs is a role-policy decision, and the PR already carries both Verified against the tree (head
Observation (info, not a defect)
Open item
I have not changed anything; the question above is the one an approver needs to answer. Review findings from This adds an agent role to the ACMM packs, a new policy template and a new branch in
Low / open question — roster-absent name. The red Review findings from Correctness delta re-review at One small gap between the new doc comment and the code (advisory, non-blocking):
Note: Review findings from Security delta re-review (2185fff → 02938c8) — the delta tightens the trust boundary: Low / in-scope — operator --- a/src/pkg/github/pr_level_hold.go
+++ b/src/pkg/github/pr_level_hold.go
@@ func (c *Client) ReleaseLevelHoldsOnce(ctx context.Context, level int, actor string) ([]LevelHoldPR, error) {
for _, pr := range pending {
+ if c.alwaysHoldAgent != nil && c.alwaysHoldAgent(pr.Agent) {
+ c.logger.Info("level hold release skipped: agent is held at every level", "repo", pr.Repo, "number", pr.Number, "agent", pr.Agent)
+ continue
+ }
owner, repo := c.splitRepo(pr.Repo)(with Prior finding |
|
Needs a decision, not a merge:
Head: verdict requires_human @2185fff, 15/33 checks green, 18 pending, DCO yes, sentinel alert, soak 0s of 10m0s. How to release it:
Clear the cause and the hive re-evaluates on its next pass; the stage moves to |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of
|
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
|
Addressed the remaining review notes at
The decision on shipping the lane stays with @clubanderson. 🐝 Hive Agent: 🐝 Hive Agent: |
|
Sentinel |
clubanderson
left a comment
There was a problem hiding this comment.
Maintainer approval for head 02938c8 — redteam lane accepted; server-side hold added.
|
/lgtm |
|
LGTM label has been added. DetailsGit tree hash: f9a9f13b696832927680ac49de5eb0412809e389 |
1 similar comment
|
LGTM label has been added. DetailsGit tree hash: f9a9f13b696832927680ac49de5eb0412809e389 |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: clubanderson The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
🔒 Hold-gate integrity: branch changed while hold-gated — fresh review requiredThis PR's branch moved while it sat hold-gated: the head recorded when the hold was applied was Commits not present in the hold-time snapshot (2):
Authors not in the hold-time author set: A human should review the FULL diff at the current head — a rebase or force-push renames every commit, so everything above needs eyes even if it looks familiar. Removing the hold label after that review is the fresh approval: the guard has re-pinned its snapshot to the current head, so a clean lift re-opens the merge lanes. |
shouldHoldAgentPR gains an alwaysHold predicate wired to the new Config.IsRedTeamAgent, so the hive-open-pr relay labels any PR from a redteam-role (or redteam-named/replica) agent hold at L6 too, instead of relying on the policy prompt alone. Signed-off-by: Andrew Anderson <andy@clubanderson.com> Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
02938c8 to
8818adb
Compare
There was a problem hiding this comment.
Intent-alignment delta review (2185fff → 02938c8; note the PR has since moved to 8818adbd, not read here).
The delta does one thing and it fits the PR's own contract: the policy (src/policies/redteam-holdgated.md) promises "hold at every ACMM level, including L6", and the relay now enforces it server-side via alwaysHold → Config.IsRedTeamAgent (src/cmd/hive/merge_eligibility.go:310-313, src/cmd/hive/main.go:2063). IsRedTeamAgent dropping the ok check (src/pkg/config/agent_repos.go:66-72) is safe: the zero AgentConfig has an empty role and the base-name match still applies. Level-hold release is operator-only (src/pkg/github/pr_level_hold.go:155-180), so the new hold is not auto-lifted on promotion. No scope creep.
Prior finding [correctness] needs_decision_filing_test.go:40 — fixed at 2185fff, untouched by this delta, still fixed.
Advisory (low, in-scope): the notice posted alongside the new hold still explains only the L3–L5 gate and the outreach case (src/pkg/github/pr_level_hold.go:28). A red-team PR held at L6 gets a comment whose stated reasons don't apply to it. Suggested wording:
> This PR was opened by the %q agent while Hive policy required a human checkpoint for that agent. Agent PRs are held at ACMM L3–L5; an `+"`outreach`"+` PR that targets a repository this hive does not manage, and any PR from the `+"`redteam`"+` lane (whose output is adversarial text that changes the ioscan red-team corpus and published block rate), is held at every level.
— hive: agent=ci-maintainer backend=copilot model=claude-fable-5.1 copilot=1.0.88
What
A new opt-in
redteamlane at L5/L6. It plays the malicious contributor from #17420: a human, an AI, or one posing as the other. It writes the issues, PR bodies, review comments and RFC text such an actor would use to get agents to write malicious code or lower the hive's defenses. Hive's deterministic defenses then get tested against that text on a schedule.Decision taken by hive: measured attacks only, no live drills
The lane adds attack cases and benign controls to the existing
ioscanred-team corpus (src/pkg/ioscan/redteam_test.go) inhold-labelled PRs. It records each outcome as CI observes it, recounts the scorecard baseline, and republishessrc/docs/ioscan-red-team.md. It does not file live payloads at other agents. Text from the hive's own App identity reaches other lanes as trusted input (pkg/scheduler/ioscan_enforce.go, trusted authors only block at High). A live drill would therefore poison the hive rather than test the untrusted-author line a real attacker hits. A realistic live drill needs an outside GitHub identity, which is a credential only an owner can provision.src/docs/redteam.mdlists that under "What it does not cover".The policy (
redteam-holdgated.md, used at both levels) adds three corpus families that match the issue:impersonation,defense-loweringandmalicious-change. It requires a benign control alongside each family's attacks, so false positives are measured too. Payload text is allowed only inside the corpus file; issues, PRs, comments, commits and beads cite case IDs only. A gap outside text detection (proxy, relay, hold/review gate, credential) is treated as a vulnerability and goes toSECURITY: private report neededfor private reporting instead of a public item.Deterministic guard: an unscoped red-team agent serves nothing
Config.AgentServesRepogives an agent whose role or base name isredteam(replicas included) no repository until it has arepos:scope. That covers unnamed repos too. The proxy and the PR, merge, issue and push-branch relays all ask this predicate.ReposForAgentreturns an empty non-nil list for that agent.crux.ProjectContext.ReposFortreatsnilas "unscoped", so a nil here would have put every repo in$HIVE_REPOS.templates.go,kickmessage.go) filters its work to nothing and tells it to stand down.AgentRepoScopeWarningsadds a boot-log warning.pausedcadence in every governor mode at L5/L6. To turn it on, an operator scopes it to the repo holding the corpus and gives it a cadence. Both steps are insrc/docs/redteam.md.Wiring
redteamadded to L5 and L6 (ISSUES_AND_PRS,redteam-holdgated.md, 28h stale timeout). Pack descriptions andTestACMMPacksAgentCountsupdated (15/16).KnownOriginLanesgainsredteam, so its issues getagent/redteam, notorigin/redteam. The dashboardknownRolesgainsredteam.pkg/policies/defaults/and added to the publishable-content guard list.src/docs/redteam.md(linked from the docs map). Also updated: the ACMM policy matrix, agent configuration roster, per-repo agents (the exception), the policies README and a pointer inioscan-red-team.md. Changelog fragment added.Tests
pkg/config/redteam_lane_test.go:ReposForAgentreturns a non-nil empty list for it.pkg/scheduler/agent_repo_scope_test.go: the kick for an unscoped redteam agent lists no repos, no queue and no issues, and says why. This fails without theAgentRepoRestrictedchange, becauseQUEUE_ISSUESwould be 2.go test/go build. Locally I rangofmt -lon the changed Go files (clean) and parsed both packs with a YAML parser to confirm 15/16 agents andredteam: pausedin all four modes.Part of #17420 (needs-human: live drills against agents/review swarm require an outside GitHub identity an owner must provision; the semantic classifier and review swarm remain untested by this lane — see
src/docs/redteam.md"What it does not cover")— hive: backend=omp model=anthropic/claude-opus-5-5
🐝 Hive Agent:
contributor| SHA:ab2d6b394