📖 Add Hive (Hive Commons) security self-assessment - #2286
clubanderson wants to merge 7 commits into
Conversation
JustinCappos
left a comment
There was a problem hiding this comment.
This needs some work to deal with parts the LLM didn't have the data for. There is also some low-hanging fruit the project could do to improve security.
| - Agents read GitHub issues, PRs, comments, labels, and diffs (untrusted text | ||
| boundary). | ||
| - Agents are "kicked" (launched) with a constructed prompt built from that | ||
| text plus repository/task context. |
There was a problem hiding this comment.
How do you deal with a party who creates a malicious issue, comment, etc.? I think you're talking about this in the run-pipeline.sh and operator-selected ACMM level, but I don't see the big picture. If I deploy this, what should I worry about?
There was a problem hiding this comment.
Fair — the document described controls without ever stating the attack path, which meant a reader had to assemble the threat model themselves. Added a section that does it directly: "If you deploy this, what should you worry about?"
It opens with the uncomfortable part rather than burying it:
Anyone with a GitHub account can open an issue or comment on a governed public repository. That text is enumerated by the pre-kick pipeline, scanned by
ioscan, and — if it survives — placed into a prompt handed to a CLI agent that holds a real, if scoped, GitHub credential. There is no step in that chain where a human necessarily reads the attacker's text first.
Then a table of concern → what actually stands in the way → what does not. The short version for your specific question about a malicious issue:
- Merge of malicious code — stopped by the proxy hard-denying
PUT /pulls/{n}/mergefor every ACMM mode, plus the GraphQL merge mutation. The agent holds no credential path to merge at any autonomy level.ioscanis explicitly not what stops this; a clever prompt beats a scanner, but it does not beat a rule table the model never sees. - Backdoored PR —
POST /pullslikewise hard-denied for every mode; creation goes throughhive-open-pras the App bot, attribution-stamped and audit-logged. But nothing prevents the content of a legitimately-created PR from being attacker-influenced, so review it like any other PR. - Secret exfiltration — mode-tiered tokens mean low-tier agents hold no code-write credential. Canaries detect prompt contents reaching outbound writes — with real limits, see the reply on your redaction comment.
I also added the three settings that actually determine exposure (ACMM level, whether the repo is public, and whether canaries/fail_mode: closed are on — both default permissive), and stated the worst realistic outcome plainly: at L6, an attacker-influenced PR authored and merged by the App bot, traceable and recoverable via attribution trailer and audit log, but still a real bad day. The guidance is to start at L1–L3 and not to start at L6 on a public flagship repo. L6 (full automation with merging) is not the goal for all repos that use hive. Hive can be tuned, at any level, to work alongside any number of maintainers to mature a codebase beyond what would be normally possible with the same quantity of maintainers.
| 2. **`ioscan` untrusted-input scanner** | ||
| (`src/pkg/ioscan`, `src/docs/ioscan.md`). Scans GitHub issue/PR/label/ | ||
| author/comment text before it enters an agent kick. Deterministic rules | ||
| (Unicode-steganography normalization, base64 decode-and-rescan, | ||
| prompt-injection phrasing, dangerous-directive and secret-shape detection) | ||
| are always the floor when enabled; blocked text is replaced with a visible | ||
| `[ioscan: content withheld — ...]` marker rather than silently dropped or | ||
| passed raw. An optional LLM-judge classifier | ||
| (`ioscan.classifier.enabled`, default `false`) adds semantic | ||
| plain-English-injection detection on top. **Enabled by default** | ||
| (`ioscan.enabled: true` is the default per `ioscan.md:9`). |
There was a problem hiding this comment.
have you red teamed to know how well this works? What should a potential user be aware of here? Is this perfect defense, works decently well, a partial mitigation, etc.?
There was a problem hiding this comment.
No, it has not been red-teamed, and there is no measured detection rate. I searched the repository for any adversarial evaluation artifact and found nothing — src/pkg/ioscan has 56 unit tests across its rule, Unicode, classifier and canary paths, but unit tests establish that known shapes are caught and say nothing about unknown ones.
So the honest characterization, now in the document: partial mitigation, unmeasured. A user should assume a determined, encoding-aware attacker defeats it.
What I'd add, because I think it matters more than the scanner's score: ioscan was never the boundary. What contains the consequence of a successful injection is the proxy's hard-deny of POST /pulls and PUT /pulls/{n}/merge for every ACMM mode — that holds whether or not the injection worked, because it is a rule table the model never sees and cannot argue with. The document now credits containment there and describes ioscan as doing three narrower things: raising the cost of casual injection, deterministically normalizing away invisible-character and homoglyph tricks, and producing an auditable record that something was withheld.
Red-team evaluation tracked in hivecommons/hive#6685 — corpus covering instruction override, Unicode steganography, homoglyphs, multi-layer encoding, smuggling via code blocks/diffs/labels/author fields, and split payloads across issue + comments; measuring what gets through and, for anything that does, whether the network deny rules still contain it. That last distinction is the finding that actually matters.
| - **Token budget** — a seven-day rolling token budget that suppresses kicks | ||
| on exhaustion, limiting denial-of-wallet from a runaway or compromised | ||
| agent loop. | ||
| - **Log scrubbing** (`src/pkg/logscrub`, `security.md`) — redacts recognized |
There was a problem hiding this comment.
Is this to prevent an model with access to these from leaking them? What's the purpose?
If so, what if I prompt inject something in which has you emit these in a base 64 encoded format or similar?
There was a problem hiding this comment.
Two separate answers, and the second one is a bug report against ourselves — your hypothetical found a real defect.
Purpose. Log scrubbing is operational hygiene for logs Hive itself writes — so a token appearing in an error string doesn't end up in a pasted log snippet, a support bundle or a CI artifact. It is not an exfiltration control, it sits on Hive's logging path rather than the agent's egress path, and the document now says so rather than letting the word "redaction" imply containment. Your base64 case defeats it completely, as would hex, reversal, or splitting across tokens.
The bug. Running your question against the control that is supposed to catch exfiltration — ioscan canaries — exposed an asymmetry we had not noticed. The input path is encoding-aware: ScanInput normalizes Unicode steganography and confusables, and scanBase64Instructions decodes base64 blobs and rescans the plaintext. The egress path is not:
// src/pkg/ioscan/canary.go
for token, c := range m {
if strings.Contains(text, token) {
return CanaryLeak{Agent: c.Agent, Token: token, Source: source}, true
}
}A bare substring test. So an agent prompt-injected into emitting its HIVE-CANARY-… token base64-encoded passes the proxy check unflagged — exactly the attack canaries exist to detect. Filed as hivecommons/hive#6686; the fix reuses the decode-and-rescan machinery already present on the input path.
Severity is moderate rather than critical only because a canary hit was always positive evidence and a miss was never proof of absence, so it degrades to "no worse than not having it" rather than to false assurance. Two related limits are now also stated in the document: canaries are default-off, and git-receive-pack bodies are pack-encoded and opaque to the scan entirely, so pushes are covered only when fail_mode: closed refuses them outright.
Thanks for this one — it's the most useful comment in the review.
| has not pursued OpenSSF Best Practices Badge ("CII Best Practices") | ||
| certification at time of writing — not yet assessed whether this is planned. |
There was a problem hiding this comment.
You should do this. At least get a passing badge please. It's not that hard and the things you need to do for this are sane security things you either are or should be doing already.
There was a problem hiding this comment.
Agreed, and you're right that the criteria are things we should be doing anyway. The honest reason it hadn't been done is that nobody had considered it — "not yet assessed whether this is planned" was an evasion, not an answer.
It's planned now and tracked in hivecommons/hive#6684. Going through the passing criteria, most are already satisfied:
- OSI-approved license (Apache-2.0)
- Public version-controlled source with interim versions
- Documented contribution process (
CONTRIBUTING.md,GOVERNANCE.md) - Private vulnerability reporting via GitHub Security Advisories
- Documented security response process (
src/docs/security-response.md) - Automated test suite gating every PR
- Static analysis in CI (golangci-lint, OpenSSF Scorecard weekly + on push)
- Dependencies pinned by digest/SHA
- No known unpatched vulnerabilities
So the outstanding work is registration and self-certification plus verifying the crypto and release-signing criteria — not engineering.
The document now says this rather than hedging, and will carry the badge ID and level once awarded instead of the issue number. I'd rather hand you a tracked commitment now than a claim.
There was a problem hiding this comment.
Correction to my reply above — I gave you a worse answer than the facts warranted.
I said the badge was planned and that the document "will carry the badge ID and level once awarded." That was wrong. The badge was already awarded at the passing level on 2026-08-27, four days before your comment. I reported pending work on something that was already done.
Verified against the public API (https://www.bestpractices.dev/projects/14261.json):
| field | value |
|---|---|
| project | 14261 (hive) |
badge_level |
passing |
badge_percentage_0 |
100 |
achieved_passing_at |
2026-08-27T17:47:12Z |
lost_passing_at |
null — never lost since |
Badge: — and it has been displayed at the top of
README.md the whole time.
So your ask — "at least get a passing badge" — was already satisfied when you made it. Silver and gold sit at 15% and 22% and I am not claiming either.
Why the wrong answer happened, since the cause is the same defect you flagged elsewhere in this review. The security self-assessment said the project "has not pursued" the badge and had "not yet assessed whether this is planned." I answered you from the document rather than from the system, so a stale document propagated into a reviewer-facing claim. That is the identical failure mode as the bus-factor answer, where OWNERS had grown to three maintainers and the document still said one. Two instances in one review is a pattern, not a coincidence: this project's self-assessment was not being re-derived from observable state.
Fixed, and a genuine problem surfaced while fixing it. The badge entry had been filled in before Hive moved out of the KubeStellar org to the vendor-neutral hivecommons org. 67 of its criteria justifications — 70 URLs — still pointed at github.com/kubestellar/hive and hive.kubestellar.io. The justification text is the entire evidentiary value of a self-certified badge; if every link a reviewer follows to check a claim lands on the old home, the badge is decorative. All 70 now resolve to the current repository and site, homepage_url and repo_url are updated, the entry re-verifies at passing / 100%, and zero fields contain the old org.
The self-assessment is corrected in hivecommons/hive#6697, which records the badge ID, level and date instead of the issue number.
One item I am deliberately leaving open rather than quietly closing: the same document still describes Hive as part of the KubeStellar CNCF Sandbox project family, which is also stale post-migration. The correct wording depends on where the sandbox application lands (cncf/sandbox#516), so I would rather flag it to you than guess at it.
| assessment identifies, and is exactly the kind of gap CNCF Incubation | ||
| review should weigh: it is a bus-factor and response-capacity risk, not a | ||
| code defect. |
There was a problem hiding this comment.
Yes, this is a huge risk. Why not mitigate it?
There was a problem hiding this comment.
It is a huge risk, and it turns out to be two risks that had been collapsed into one. One was already mitigated before you reviewed — our document was simply stale. The other is real, unmitigated, and now stated as the project's largest remaining process risk.
The bus-factor half was fixed three days before this review, and the document failed to say so. Timeline:
| Date | Event |
|---|---|
| 2026-08-27 | This PR opened; document says one approver |
| 2026-08-28 | OWNERS grows to three maintainers (hivecommons/hive#4996); security-response.md added (#5005) |
| 2026-08-31 | This review |
The Maintainer Committee is now @clubanderson (IBM), @hanthor (Universal Blue) and @Danathar (independent) — three people across three affiliations, which improves vendor neutrality as well as bus factor — and security-response.md names the committee as the security response team with an explicit "whoever picks up the advisory drives it, and loops in the others for severity and disclosure timing" rule. That's on us for submitting a document that was obsolete on arrival, not on you for flagging what it said.
The enforcement half is not mitigated, and I don't want to bury it under the good news. I checked the live v4 branch protection API: required_pull_request_reviews is null — there are no required reviews at all, code-owner or otherwise. .github/CODEOWNERS covers the security-sensitive paths but is inert, and every entry still resolves to one owner. Concretely: a change to src/pkg/proxy/rules.go — the deny-rule table this document credits as the control an attacker cannot argue with — can merge on green CI with no human security reviewer.
Why it was left off: Hive is substantially self-hosting, and its own agent fleet merges green PRs, so blanket required review halts the repository. That's an explanation, not a justification, and I've written it that way in the document.
The resolution being pursued is scoped rather than all-or-nothing — enforce code-owner review only on src/pkg/proxy/**, the SUID contract script, Dockerfile*, .github/workflows/**, src/deploy/** and key/cookie handling, leaving ordinary paths on the automated merge path — plus expanding CODEOWNERS to all three maintainers so enforcement doesn't bottleneck on one person. Tracked in hivecommons/hive#6687.
Until that ships, the document tells evaluators to treat "the deny-rule table is enforced in code" as true and "the deny-rule table is protected from unreviewed change" as false.
| Not applicable — Hive does not currently publish named case studies of | ||
| production deployments in the Hive repository. (Not the same claim as "Hive has | ||
| no production users" — simply that no case-study document exists to cite | ||
| here.) |
There was a problem hiding this comment.
Looks like AI generated this for you. You should know the answer here and be able to write a correct response
There was a problem hiding this comment.
You were right, and it was worse than a style problem — the claim was factually false.
"Not applicable — Hive does not currently publish named case studies" is contradicted by ADOPTERS.md in the same repository, which lists seven adopters, three at Production maturity. In a document whose entire value is candor, that's the worst kind of error to ship.
Replaced with the real table. Two entries deserve a reviewer's attention specifically:
- tunaos.org — a self-hosted hub coordinating two spokes at ACMM L5/L6 across 43 repositories. Those are the autonomy tiers where agents can merge, which makes it the strongest available evidence that the deny-rule and attribution controls hold outside the maintainers' own environment. Production evidence in hivecommons/hive#5773.
- Project Bluefin — independently documented by the adopter at docs.projectbluefin.io/factory rather than merely self-reported by us.
Also KubeStellar Console (Production), plus Frostyard, Open Horizon, Open Horizon Services and Danathar at Pre-production.
What we genuinely don't have is a security case study — a narrative of an incident, an attempted injection, or an adopter's own security evaluation. That would be more useful to TAG-Security than an adopter table, and the document now says its absence is a real gap rather than pretending the question didn't apply.
| Items this assessment could not verify from the repository alone, listed | ||
| explicitly rather than guessed at: | ||
|
|
||
| - Whether issue #3760 (cited as the reason SBOM/provenance attestation is | ||
| disabled on container builds) documents a planned path to re-enabling SBOM | ||
| generation, or is a permanent decision. | ||
| - Whether any external channel (mailing list, Slack, Matrix) exists for | ||
| security-relevant project communication beyond GitHub issues/PRs and | ||
| private security advisories. | ||
| - Whether a formal, security-specific incident-response runbook (as distinct | ||
| from the disaster-recovery runbook at `docs/HUB_DISASTER_RECOVERY.md`) | ||
| exists outside the Hive repository (e.g., in an internal-only document) — | ||
| none was found in the public repository. | ||
| - Whether the project intends to pursue OpenSSF Best Practices Badge | ||
| certification. | ||
| - The current numeric OpenSSF Scorecard result — intentionally not quoted | ||
| here since it changes independently of this document; consult the live | ||
| badge/workflow output. | ||
| - Whether any prior informal security review (e.g., a maintainer's own | ||
| adversarial testing beyond what is documented in the ADRs and threat | ||
| model) occurred without producing a public artifact. This assessment | ||
| treats "no third-party audit" as accurate based on the absence of any | ||
| audit report in the repository, but cannot rule out unpublished internal | ||
| review. | ||
| - Whether `.github/CODEOWNERS` enforcement (branch-protection "Require | ||
| review from Code Owners") is planned to be enabled once the project has | ||
| more than one active human maintainer, or is intended to remain advisory | ||
| indefinitely regardless of maintainer count. |
There was a problem hiding this comment.
This is the LLM telling you to clarify these (as I see it). Please go through and figure these out and update the document before we merge it.
There was a problem hiding this comment.
will do - Thank you for the review
There was a problem hiding this comment.
Correct on both counts — those were prompts to the authors wearing a findings section's clothes, and a couple of them had answers sitting in the repository the whole time.
The section is gone. Every item was run down and answered in "Questions resolved since first review." Summary:
| Question | Answer |
|---|---|
| Does #3760 document a path to re-enabling SBOM? | Closed, and it was never an SBOM decision. It's the bug report for /usr/local/bin/hive failing to exec with EPERM under containerd/k3s and rootless podman — attestations force an OCI image index, which permitted an overlayfs metacopy redirect presenting the binary as non-executable. Disabling them fixed a crash loop. Correct path is out-of-band SBOM generation (#6688), plus a CI assertion on manifest media type so the constraint stops being comment-only. |
| Any external communication channel? | No, by design. CONTRIBUTING.md directs all discussion to GitHub issues/PRs "so decisions remain public and searchable." The Slack/Discord integrations are operator-alerting features of the software, not project channels — I'd conflated the two. |
| Security-specific incident-response runbook? | Yes — and it didn't exist when the draft was written. security-response.md landed 2026-08-28. Still missing: post-incident review practice and user-notification SLA for a confirmed compromise. |
| Pursuing the OpenSSF badge? | Yes, now tracked in #6684. |
| Current Scorecard score? | Deliberately still not quoted — the workflow runs weekly and on every push to v4, and freezing a number makes the document wrong on a schedule. This is the one item I left unanswered on purpose, and the document says why. |
| Any informal security review or adversarial testing? | No — and this is the most significant "no" in the list. No red team, no adversarial evaluation, no measured detection rate for ioscan anywhere in the repository. The hardening work cited by issue number throughout the threat model is maintainer-identified and maintainer-fixed, which is not adversarial review. Tracked in #6685. |
| Will CODEOWNERS enforcement be enabled? | Live v4 branch protection currently requires no PR reviews at all. Scoped enforcement over security-critical paths tracked in #6687. |
Two of those answers are "no," which is the point — they're findings, and they read better as findings than as questions.
|
@clubanderson and others from the team. please let me know when you've addressed the prior comments and we can provide another review. |
Revision 2 in response to the seven review comments on cncf#2286. - Add a deployer-facing threat section: the attack path from a stranger's issue to a repository write, what stands in the way of each concern and what does not, the three settings that determine exposure, and the worst realistic outcome at L6. - State plainly that ioscan has never been red-teamed and has no measured detection rate; recharacterize it as a partial mitigation and credit containment to the network deny rules instead. - Re-scope log scrubbing as log hygiene rather than an exfiltration control, and answer the base64 question directly. Running that question against the canary path surfaced a real defect: the egress check is substring-only, so an encoded canary is not detected, while the input path does decode base64. - Replace the false "Case studies: not applicable" claim with the seven adopters in ADOPTERS.md, three in production, one at ACMM L5/L6 across 43 repositories. - Record that the maintainer roster grew from one to three across three affiliations and that a security response process now exists; restate the remaining risk as unenforced code ownership rather than bus factor. - Correct the SBOM explanation: #3760 is a container-runtime exec defect caused by attestations forcing an OCI index, not an SBOM deprioritization. - Delete the open-questions section and answer every item against the repository, including two answers that are "no". - Commit to the OpenSSF Best Practices badge. Tracking issues filed: hivecommons/hive#6684, #6685, #6686, #6687, #6688. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andy Anderson <clubanderson@gmail.com>
|
@JustinCappos — all seven comments are addressed and replied to individually. Ready for another review. Short version of what changed, and one thing you should see first. Your base64 question found a real bug. You asked, about secret redaction, "what if I prompt inject something in which has you emit these in a base 64 encoded format or similar?" Running that against the control that's actually supposed to catch exfiltration — The rest:
Five issues filed: #6684 badge · #6685 red team · #6686 canary bug · #6687 code-owner enforcement · #6688 out-of-band SBOM. The canonical copy is updated in lockstep at hivecommons/hive#6692. All 37 external links verified 200, all internal anchors resolve. The document is meaningfully less confident than it was, which I think is the correct direction. Thanks for the review — it was substantive, and it improved the software and not just the document. |
* Revise security self-assessment after TAG-Security review Mirrors the revision made for cncf/toc#2286 into the canonical copy. Seven review comments asked for the parts the first draft hedged on: - New deployer-facing section: the path from a stranger's issue to a repository write, what stands in the way of each concern and what does not, the three settings that determine exposure, and the worst realistic outcome at L6. - ioscan efficacy is now stated as unmeasured. No red-team exercise exists, so it is described as a partial mitigation, with containment credited to the proxy deny rules rather than to the scanner. - Log scrubbing re-scoped as hygiene for Hive's own logs, explicitly not an exfiltration control. Answering the reviewer's base64 question against the canary path surfaced a real defect, filed as #6686: the egress check is a substring test while the input path decodes base64. - "Case studies: not applicable" was wrong. ADOPTERS.md lists seven adopters, three in production, one at ACMM L5/L6 across 43 repositories. - Records the roster growth from one maintainer to three across three affiliations, and security-response.md; restates the remaining risk as unenforced code ownership rather than bus factor. - Corrects the SBOM explanation: #3760 is a container-runtime exec defect caused by attestations forcing an OCI index, not an SBOM decision. - Open-questions section replaced with answers, including two that are "no". Tracking issues: #6684, #6685, #6686, #6687, #6688. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andy Anderson <clubanderson@gmail.com> * Fix changelog fragment: must start with a '- ' entry bullet Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andy Anderson <clubanderson@gmail.com> --------- Signed-off-by: Andy Anderson <clubanderson@gmail.com> Co-authored-by: Andy Anderson <clubanderson@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pushed a correction to this self-assessment: the OpenSSF Best Practices badge status was wrong in this document, in the direction of understating the project. The document said the badge was "not yet held, and now being pursued." It has in fact been held at the passing level since 2026-08-27 — project 14261, 100% of the passing criteria, never lapsed — which is four days before the review that asked for it. @JustinCappos — this is the correction to my reply on your badge comment; you asked for a passing badge and we already had one, so answering "we'll get it" was wrong. Separately corrected there. What was actually outstanding turned out to be the badge entry, not the badge. Hive moved out of the Root cause, since it is the same defect you flagged elsewhere in this review. This document was being written from itself rather than re-derived from observable state. That is what produced the stale maintainer count, and it produced this too — the CNCF Sandbox application (cncf/sandbox#516) has stated "OpenSSF Best Practices passing badge at 100%" correctly since filing, so the accurate fact was already written down in a sibling document. The badge status in this file is now taken from the programme's public API, and the section records both prior errors rather than quietly overwriting them. Consistency pass. Correcting the one section left three other passages contradicting it, including the response table at the top of this document. Those are fixed in hivecommons/hive#6715, and this file has been regenerated from the canonical copy so the two agree exactly. Affiliation is consistent across all three places: Hive is an independent project in the |
CNCF TAG-Security asked, on the ioscan section of the self-assessment (cncf/toc#2286): "have you red teamed to know how well this works? Is this perfect defense, works decently well, a partial mitigation, etc.?" The honest answer was no. pkg/ioscan has 56 unit tests, but they assert that KNOWN shapes are caught — by construction every one passes, and they cannot produce a detection rate. The self-assessment recorded efficacy as "unmeasured" and committed to measuring it. Measured now, against 43 adversarial payloads through the production entry point (ioscan.ScanInput, shipped defaults): blocked (withheld from the agent) 16 37% detected but injected anyway 8 19% missed entirely 19 44% ------------------------------------------------ reached the agent verbatim 27 63% Per family, detection is very uneven: unicode 6/8 blocked, smuggling 4/7, direct-override 2/10, split-payload 0/4. Plus 2 of 6 benign controls were redacted — including an issue *reporting* a prompt-injection bug. Three findings worth more than the headline number: - The middle bucket is not obvious. blockedInput requires Critical, or Injection at High+, so every Medium finding is recorded for audit and the raw text is still injected. "ioscan findings" is not "attacks stopped", and nothing said so before. - Split payloads score 0% structurally, not for want of a rule. Each untrusted segment is scanned separately, so a phrase split across two comments is never a single string to match. - Two single characters (U+FF29 fullwidth I, U+00AD soft hyphen) defeat the STRONGEST family outright. Filed as #6708; the U08 fix is verified to be a one-character addition to isInvisibleControl. Item 3 of the issue — does the network layer still contain what gets through — is measured rather than claimed: TestRedTeamContainmentHoldsForEveryMode checks POST /pulls and PUT /pulls/{n}/merge against all four ACMM modes on every run. That is the real finding: the injection defense of record is the proxy deny rule, not the text scanner. The corpus is a RATCHET, not a snapshot. TestRedTeamCorpusOutcomesAreStable pins each observed outcome and fails in BOTH directions — a regression, and equally an improvement, because an improvement makes the published miss rate wrong. Both branches were verified by deliberately breaking and deliberately improving a rule; the improvement path prints "re-run the report and republish". So the document's numbers cannot silently drift from the code. Not measured, and said so in the document rather than estimated: the optional LLM classifier (default-off, and fails open, so the deterministic floor here is the worst case either way), the output path, and the canary egress path (#6686). Fixes #6685 Signed-off-by: Danathar <Danathar@users.noreply.github.com>
|
Follow-up correction to my earlier note in this thread — two numbers I cited there have since been superseded, and the mirror is now re-synced from canonical ( 1. The red-team figures moved, because the gaps they measured were closed. My earlier comment quoted the evaluation's original result. Since then hivecommons/hive#6714 closed the two Unicode gaps that evaluation found — U+FF29 FULLWIDTH LATIN CAPITAL I was missing from the confusable fold table, and U+00AD SOFT HYPHEN was not treated as an invisible control. Each defeated the entire Unicode family with a single character.
The overall characterisation is unchanged and deliberately so: this is still a partial mitigation, and a narrow one. 58% of attack payloads still reach the agent verbatim, paraphrased instruction override is still 2/10, and split payloads are still 0/4 — structurally, because each untrusted segment is scanned separately. What bounds the consequence of a successful injection is not 2. The canary egress weakness recorded here has been fixed. Revision 2 recorded, as a known weakness, that the canary egress check matched literal substrings only — so a base64-encoded canary passed unflagged (#6686). That is now closed: 3. Why there were stale numbers to correct at all. Worth stating plainly, since this is the same class of problem @JustinCappos flagged originally with the stale maintainer count. The regression test that publishes these outcomes fails when a result changes in either direction, including improvements, precisely so an improvement cannot silently invalidate a published figure. It worked — but the republish updated the results table and the machine-readable outcomes while leaving the prose that summarised them untouched, so the document contradicted itself within three lines of the corrected table, and the stale figures had already been copied into this mirror. Fixed in hivecommons/hive#6741 as a repository-wide sweep for the figures rather than an edit to the one file that happened to be noticed, and verified with This mirror is generated from the canonical copy at |
sherine-k
left a comment
There was a problem hiding this comment.
hi @clubanderson
Thank you for this PR and for the recent updates you have made.
I have added a few questions, especially to make sure that all angles of the threat model were equally addressed.
A comment on the style rather than the content: Some sections looked somewhat dense and harder to read for me. Do you think you could do something?
| - **Hub operators / SaaS platform operators** — run the central hub that | ||
| coordinates registered spokes and, for hosted spokes, provisions | ||
| infrastructure and injects GitHub App credentials. |
There was a problem hiding this comment.
I was wondering if any threats were identified on this interface between hub and spokes.
There was a problem hiding this comment.
Yes — and the document under-describes this interface. What is in place, and what is not:
Spoke → hub. Each spoke authenticates its heartbeat with a per-hive derived bearer (keyderive.PerHiveKey, bound to trust domain + hive ID), verified across live key generations so the master can rotate without a flag day (pkg/hub/hub_keys.go). The fleet-wide legacy bearer lane has been deleted. Heartbeats carry operational telemetry only — never credentials.
Hub → spoke. The hub returns config — and, for hosted spokes, GitHub App credentials — in the heartbeat response. Today the spoke trusts TLS to its configured hub URL for that; the response body is not independently signed or bound to a hive ID / sequence. The tokens the hub mints (SSO, delegation) are asymmetric — Ed25519, spokes hold only the public key — but that does not extend to the config push.
Threats we will list in the document
| Threat | Status |
|---|---|
| Hub compromise → fleet-wide credential/config push | Real; the hub is the highest-value asset. Mitigated only by hub hardening and the fact that a spoke gets its own App key alone. |
| Spoke impersonation to the hub | Bounded: per-hive keys mean one leaked bearer affects one hive; the hub cannot be made to accept another hive's identity with it. |
| Replay / rollback / mis-delivery of a pushed config | Open. Filed as hivecommons/hive#7082: sign the heartbeat response with the hub's existing Ed25519 key and bind it to hive_id + a monotonic sequence. |
| - **Contributors via ClankeR relay** — external contributors who donate | ||
| compute by running an agent against a hive's queue over a relay protocol | ||
| (see [contributor-relay.md](https://github.com/hivecommons/hive/blob/v4/src/docs/contributor-relay.md)). |
There was a problem hiding this comment.
How does hive authenticate a contributor/relay?
There was a problem hiding this comment.
The relay is authenticated with a per-contributor registration token; the doc should say how, so here it is and it will be added.
- A contributor registers once against a GitHub identity. The hive issues a registration token and stores only its SHA-256 hash in the contributor profile (profiles are 0600 — they also carry PII). The plaintext is shown once and lives in the contributor's
~/.config/hive/contributor.env. - The relay presents the token as a bearer when it connects to
wss://<hive>/api/contribute/ws; the hive hashes and compares in constant time (contributorProfileFromRegistrationToken). - Every contributor has a trust tier with per-tier rate limits and model admission (
contributor-trust-and-roles.md). - Per task the hub mints a 55-minute scoped GitHub token for the PR push and re-mints it while the task is live; a task is abandoned after 30 minutes without forward progress or a 4-hour absolute backstop, and the lease is fenced to that contributor identity.
Weaknesses, stated: the registration token is a long-lived bearer with no expiry or rotation today, and the contributor's machine is outside our trust boundary — a malicious relay can submit a malicious PR. That is why relay output enters at exactly the same PR-review gate as any external contributor's PR and is never merge-eligible on its own.
| - **Repository maintainer / hive operator** — configures ACMM autonomy level, | ||
| which repositories are governed, which agents run, and credential sources | ||
| (GitHub App vs. PAT, inference backend keys). |
There was a problem hiding this comment.
This is also an area that could be worth expanding on for the self assessment:
where are GH tokens and inference keys stored? What can be compromised if hive operator is compromised? What are threats that hive operators face while installing / updating ?
There was a problem hiding this comment.
Good question — the document names the credentials but never says where they live or what falls if each actor falls. Answering in three parts; the document will be updated to carry this.
Where they are stored
| Credential | Hosted spoke | Hub | Self-hosted |
|---|---|---|---|
| GitHub App private key | Projected from the hive-secrets Kubernetes Secret, whole-volume mount at 0440 with pod fsGroup. A spoke receives only its own App key — the earlier fleet-wide key broadcast was removed (audit finding N1, CWE-200). |
One 0600 file per cluster, keyed by cluster ID; never rendered into any spoke manifest. | hive.yaml github.key_file or env; operator-managed. |
| Inference keys | Same hive-secrets Secret; dashboard-entered keys are written by a scoped hive-secrets-writer role. |
n/a | hive.yaml / env. |
Hub master (HIVE_HUB_SECRET) |
Never leaves the hub. Spokes get derived sub-keys (pkg/keyderive, domain-separated per hive): a heartbeat bearer, a session-verify key, and the SSO public key. |
Hub only. | n/a |
Blast radius
- Spoke operator compromised → that spoke's App installation scope (the repositories it governs), its dashboard token, its inference keys. Not other tenants' keys, not the hub master, and cannot mint an admin session or an SSO-as-any-owner token (those keys are hub-only).
- Hub compromised → the worst case in the system: the hub provisions spokes and pushes config and App credentials to them on the heartbeat lane, so a hub compromise is fleet-wide. That asymmetry belongs in the threat model explicitly and will be added (see the reply on L113).
Install / update threats
- Base images pinned by digest (
FROM golang@sha256:…,node@sha256:…); CLI backends pinned by version, and by per-arch SHA-256 where upstream publishes one (Goose, muse — mismatch hard-fails the build); npm installs run with--ignore-scripts. - Upgrades are pull-based: the spoke patches its own Deployment through a namespaced ServiceAccount whose role is
get/patchon that one Deployment; the hub does not need cluster credentials to a spoke's cluster to upgrade it. - Gap, already in the document and repeated here: SBOM/provenance attestations are disabled on image builds (#3760), so the supply chain is pinned but not attestable end-to-end.
| **How well does it work? Measured: a partial mitigation, and a narrow one** | ||
| — see [ioscan-red-team.md](https://github.com/hivecommons/hive/blob/v4/src/docs/ioscan-red-team.md) for the corpus, the | ||
| methodology and the full per-case table. Against 43 adversarial payloads |
There was a problem hiding this comment.
I probably haven't thoroughly read ioscan-red-team.md, but this section feels like duplicating the content of that doc?
If so, it might be nicer to just summarize the gist here, and let the reader review more by following the link
There was a problem hiding this comment.
Agreed — it grew in place while answering the first round and now duplicates the source. I will cut it to the gist (corpus size and families; 42% withheld / 58% reached the agent; Unicode 8/8 vs the weak families named; containment credited to the proxy) and point at ioscan-red-team.md for methodology and the per-case table.
| |---|---|---| | ||
| | Attacker text talks an agent into **merging** malicious code | `PUT /pulls/{n}/merge` is **hard-denied for every ACMM mode** at the proxy (`rules.go:215-226`), as is the GraphQL merge mutation. The agent never holds a credential path to merge; `hive-merge` runs as Hive with a SHA-pinned eligibility binding. | `ioscan`. A sufficiently clever prompt defeats a scanner; it does not defeat a rule table the model never sees. | | ||
| | Attacker text talks an agent into **opening a PR** with a backdoor | `POST /pulls` is likewise hard-denied for every mode. PR creation goes through `hive-open-pr` as the App bot, attribution-stamped and audit-logged. | Nothing prevents the *content* of a legitimately-created PR from being attacker-influenced. Review it like any other PR. | | ||
| | Attacker exfiltrates your **secrets** via the agent | Mode-tiered tokens mean low-tier agents never hold code-write credentials. Optional `ioscan` canaries detect prompt contents echoed into outbound GitHub writes at the proxy, including base64/hex/URL-encoded, reversed and split spellings of the token. | Canaries are **default-off**, and their transform list is finite — an encoding it does not model still passes. `git-receive-pack` bodies are unscannable, and non-GitHub egress is tunneled without inspection. | |
There was a problem hiding this comment.
What would the impact of setting fail_mode: closed and ioscan.canaries to enabled by default be?
Are these disabled by default because they are only partial mitigations?
Are there plans to improve the security provided with these 2 modes?
There was a problem hiding this comment.
Fair questions, and one correction to how the document frames the first mode.
fail_mode: open does not pass the injection through. On a Critical finding, open redacts the offending text and continues the kick; closed blocks the kick and records an ioscan_fail_closed audit entry. The tradeoff is availability: closed-by-default turns every Critical false positive into a stalled queue item that needs an operator, and on public repositories Critical fires rarely but not never. The document's current wording ("continue to process untrusted input") overstates the exposure and will be corrected.
Canaries are off by default for a plainer reason: the egress half was not yet trustworthy enough to make everyone pay for it. Justin's base64 question in the first review round exposed exactly that — the input path decodes base64 and normalizes Unicode, the egress check was a bare strings.Contains (hivecommons/hive#6686). Shipping that on by default would have given a false sense of coverage.
Yes, they are partial mitigations, and the document is explicit that containment is the proxy's hard-deny rules, not ioscan (ioscan-red-team.md: 58% of adversarial payloads reach the agent; the network boundary is what held in every run).
Plan, now tracked as hivecommons/hive#7083:
- After #6686 lands,
ioscan.canariesdefaults on (explicitfalsestill opts out). fail_mode: closedbecomes the default at ACMM ≥ L5 — the levels where agents can merge — while L1–L4 stayopen. Per-hive override remains.
There was a problem hiding this comment.
Correction to my reply above, caught while folding it into the document: #6686 is already fixed — closed 2026-09-11 by hivecommons/hive#6701 and #6720, before this review round. The canary egress check now normalizes and decodes outbound bodies (base64 incl. nested/URL-safe, hex, percent-encoding, reversal, homoglyph/zero-width, separator splitting) before matching. The document already said so; I answered from memory of the first round. Apologies.
So the "after #6686" precondition is gone, and the plan in hivecommons/hive#7083 simplifies to: flip ioscan.canaries on by default now, and make fail_mode: closed the default at ACMM ≥ L5. Issue updated accordingly. The tradeoff statement stands: both are partial mitigations, the transform list is finite and enumerated, and containment remains the proxy deny rules.
| - **Container build**: multi-stage `src/Dockerfile`, digest-pinned base | ||
| images (`golang:1.27-alpine@sha256:...`, `node:26-slim@sha256:...` ×2), a | ||
| build-time SUID-contract check that fails the build on any unexpected | ||
| setuid/setgid binary, and checksum-verified tool downloads (tmux, ttyd, | ||
| `gh`, goose, `su-exec`). | ||
| - **No SBOM/provenance attestation** is attached to built images — explicitly | ||
| disabled (`sbom: false`, citing issue #3760) rather than merely absent by | ||
| omission. Release SBOMs are generated out-of-band as standalone SPDX JSON | ||
| files and attached to the GitHub Release, so the published image manifests | ||
| stay plain. |
There was a problem hiding this comment.
The container image is definitely more secure with the above in place.
The footprint of the image is rather big, with codex, claude, agy, goose, copilot, node all bundled together.
Is that by design ? ie all in one pod?
There was a problem hiding this comment.
By design for the resident spoke, yes — and the cost is real, so both belong in the document.
Why one image. A spoke runs several agent roles in one pod, one tmux session per agent, and the operator picks the backend per role (e.g. Copilot for the scanner, Claude for quality, Codex for review). Bundling every supported CLI lets that be a config change rather than an image rebuild, and lets a hosted spoke switch backends when a provider is degraded.
What it costs. A large surface in one image, and no per-agent kernel boundary — agents are separated by UID, not by container (already listed as weakness #2, hivecommons/hive#2804). Mitigations in place: non-root UIDs, npm --ignore-scripts, version/digest pinning with per-arch SHA-256 where upstream publishes it, and the MITM proxy as the egress boundary regardless of which CLI is talking.
Direction. The contributor path already ships a separate, slimmer Dockerfile.contributor. For the resident spoke the roadmap is per-backend image variants so an operator running only Copilot does not carry Codex/Claude/Goose; the ACMM pack already knows which backends a level uses, so the selection can be automatic. I will add this as a stated tradeoff in the components section rather than leaving the reader to infer it from the Dockerfile.
|
@sherine-k — thank you for the second pass. All six inline questions are answered individually; two of them surfaced things worth tracking rather than just describing, so they now have issues:
On readability: agreed. The next push to this PR will (a) fold the answers above into the document — a credential-storage/blast-radius table, a hub↔spoke threat table, the relay authentication paragraph, the image tradeoff — (b) collapse the red-team section to a summary that links to |
|
@sherine-k — the revision is up (
A Revision 3 table at the top maps each point to its change, same as Revision 2 did for Justin's round. Ready for another look. |
cncf/toc#2286 second review (sherine-k) asked six questions the document had answered by control rather than by consequence. Changes: - New "Credentials: where they live and what falls with them": storage table per credential per deployment shape, blast-radius table per compromised actor (spoke / relay / hub), install and upgrade facts. - New "Hub <-> spoke interface": both directions and the hub-minted tokens, plus a threat table. Finding: hub->spoke heartbeat responses carrying config and App credentials rely on TLS alone and are not signed or hive-bound. Filed as #7082. - New "Contributor relay authentication": hashed registration token, constant-time compare, trust tiers, 55-minute per-task token, and the stated weaknesses. - Corrected the document's own framing of fail_mode: open in three places -- it REDACTS a Critical finding and continues, it does not pass the injection through. Defaults plan (canaries on everywhere, closed at L5+) tracked in #7083. - Red-team paragraph in critical component 2 replaced with a summary table linking to ioscan-red-team.md. - "One image, every backend" stated as a tradeoff with cost, mitigations and roadmap. - Appendix answer "no adversarial testing has occurred" was stale since #6685 closed; now says what was done and what still has not been (external review). - Revision 3 table at the top mapping each review point to its change. Link checker: all relative links and anchors resolve. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andy Anderson <andy@clubanderson.com>
Adds the CNCF TAG-Security self-assessment for KubeStellar Hive under projects/kubestellar/sub-projects/hive/security-assessment/, alongside the existing Console assessment. Hive orchestrates fleets of AI coding agents that autonomously maintain software projects — filing issues, opening pull requests, reviewing, and at high autonomy levels merging. The security model is therefore unusual for a CNCF project: it runs model output with credentials that can write to source repositories, so the assessment focuses on the boundaries that contain that. Covered: the ACMM autonomy levels and agent mode ladder that gate write and merge capability, ioscan prompt-injection scanning of untrusted input, the MITM proxy enforcing network egress rules, GitHub App credential handling, the append-only audit log that provides attribution, and the private vulnerability reporting process. The assessment states known weaknesses rather than omitting them, including that ioscan's default fail mode is open, that agents share a container rather than being sandboxed per run, and that the project currently has a single maintainer. It also records that no third-party security audit or penetration test has been performed. Source document: https://github.com/kubestellar/hive/blob/v4/src/docs/security-self-assessment.md Relative links from the source have been rewritten to absolute Hive URLs so they resolve from this repository. Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Hive was transferred out of the KubeStellar org to its own vendor-neutral org, hivecommons, on 2026-09-03, and is applying to Sandbox as an independent project (cncf/sandbox#516). It is no longer a KubeStellar subproject, so the assessment moves from projects/kubestellar/sub-projects/hive/ to projects/hive-commons/. - Rewrite all kubestellar/hive source links to hivecommons/hive - Reframe scope, ecosystem, and related-projects sections: KubeStellar is now described as Hive's origin and first production adopter rather than its host org - Correct "Incubation application" to Sandbox application (cncf#516) - Point the sibling Console assessment link at an absolute URL, since the previous relative path no longer resolves from the new location - Fix a pre-existing broken link: docs/landscape.md to src/docs/landscape.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Revision 2 in response to the seven review comments on cncf#2286. - Add a deployer-facing threat section: the attack path from a stranger's issue to a repository write, what stands in the way of each concern and what does not, the three settings that determine exposure, and the worst realistic outcome at L6. - State plainly that ioscan has never been red-teamed and has no measured detection rate; recharacterize it as a partial mitigation and credit containment to the network deny rules instead. - Re-scope log scrubbing as log hygiene rather than an exfiltration control, and answer the base64 question directly. Running that question against the canary path surfaced a real defect: the egress check is substring-only, so an encoded canary is not detected, while the input path does decode base64. - Replace the false "Case studies: not applicable" claim with the seven adopters in ADOPTERS.md, three in production, one at ACMM L5/L6 across 43 repositories. - Record that the maintainer roster grew from one to three across three affiliations and that a security response process now exists; restate the remaining risk as unenforced code ownership rather than bus factor. - Correct the SBOM explanation: #3760 is a container-runtime exec defect caused by attestations forcing an OCI index, not an SBOM deprioritization. - Delete the open-questions section and answer every item against the repository, including two answers that are "no". - Commit to the OpenSSF Best Practices badge. Tracking issues filed: hivecommons/hive#6684, #6685, #6686, #6687, #6688. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Mirrors the canonical document from hivecommons/hive after correcting the badge status. The mirror said the badge was not yet held and being pursued; it was in fact awarded at the passing level on 2026-08-27, project 14261, four days before the TAG-Security review that asked for it. Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Re-mirrors the canonical document after the badge-consistency pass in hivecommons/hive. Brings across the measured ioscan red-team results and makes all badge statements agree: project 14261, passing, 100%, awarded 2026-08-27. Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Canonical hivecommons/hive v4 now reports 42% of attack payloads withheld and 58% reaching the agent (Unicode family 8/8) after #6714 closed the fullwidth-capital and soft-hyphen gaps, and records the canary egress check as encoding-aware after #6686 was fixed. This mirror still carried the superseded 37%/63%, 6/8 and substring-only text. Also rewrites two relative releases.md links that resolve nowhere in this repository. Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…urity review) Mirrors hivecommons/hive#7088. Adds credential storage and blast-radius tables, hub<->spoke interface threat table (#7082), contributor relay authentication, corrected fail_mode framing (#7083), condensed red-team summary, image-composition tradeoff, and a readability pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
6eaa9cb to
9c772ad
Compare
Two ioscan hardening modes shipped off-by-default; the CNCF TAG-Security self-assessment (cncf/toc#2286) asked why and whether that would change. - ioscan.canaries now defaults ON. It was off because the egress check was not encoding-aware (#6686); that reason has expired — CanaryRegistry.Scan now normalizes and decodes outbound bodies before matching (#6701, #6720). Mirror the ioscan.enabled *bool pattern: Canaries is now *bool, nil == on, explicit false still opts out (CanariesEnabled()). - ioscan.fail_mode now defaults to closed at ACMM L5/L6 (the levels where agents can merge), via the L5/L6 packs' governor.ioscan_fail_mode, staying open at L1-L4. Expressed through the same pack-governor mechanism as plan_auto_approve (IoscanFailModeForLevel), resolved by FailClosedAtLevel; an explicit operator fail_mode overrides the level default in either direction at every level. Docs (security-self-assessment.md, acmm-policy-matrix.md, ioscan.md, hive.yaml.example) document both defaults with the tradeoff stated plainly: closed-by-default turns every Critical false-positive at L5/L6 into a stalled queue item that an operator must clear by hand. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Two ioscan hardening modes shipped off-by-default; the CNCF TAG-Security self-assessment (cncf/toc#2286) asked why and whether that would change. - ioscan.canaries now defaults ON. It was off because the egress check was not encoding-aware (#6686); that reason has expired — CanaryRegistry.Scan now normalizes and decodes outbound bodies before matching (#6701, #6720). Mirror the ioscan.enabled *bool pattern: Canaries is now *bool, nil == on, explicit false still opts out (CanariesEnabled()). - ioscan.fail_mode now defaults to closed at ACMM L5/L6 (the levels where agents can merge), via the L5/L6 packs' governor.ioscan_fail_mode, staying open at L1-L4. Expressed through the same pack-governor mechanism as plan_auto_approve (IoscanFailModeForLevel), resolved by FailClosedAtLevel; an explicit operator fail_mode overrides the level default in either direction at every level. Docs (security-self-assessment.md, acmm-policy-matrix.md, ioscan.md, hive.yaml.example) document both defaults with the tradeoff stated plainly: closed-by-default turns every Critical false-positive at L5/L6 into a stalled queue item that an operator must clear by hand. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Andrew Anderson <andy@clubanderson.com>
The hub→spoke heartbeat response — which carries config and, for hosted spokes, GitHub App credentials — was authenticated by the TLS channel alone. A TLS-terminating middlebox or a misconfigured HIVE_HUB_URL could push arbitrary config/credentials to a spoke, a response captured for hive A could be replayed to hive B, and an old response could be replayed to roll config back (cncf/toc#2286, TAG-Security second review). Sign the response body with the hub's EXISTING Ed25519 key already derived from the master seed (infoSSOEd25519Seed) — the same key used for SSO and session tokens. No new key, no new distribution, no new primitive. Spokes verify with the public key they already hold (HIVE_SSO_PUBLIC_KEY). The signed body is bound to hive_id (defeats cross-hive replay) and a per-hive monotonic seq/timestamp (defeats rollback replay). The signature is detached in the X-Hive-Heartbeat-Signature header, computed over the exact body bytes, so the spoke re-hashes what it received rather than re-encoding JSON — following pkg/delegation/token.go's authenticate-before-parse order. Rollout is staged and safe by default. Spokes default to log-only (HIVE_HEARTBEAT_VERIFY=log): verify, log/metric on failure, still accept. Enforcement is opt-in (HIVE_HEARTBEAT_VERIFY=enforce) and a spoke rejects unsigned/mis-bound/stale responses only after it has accepted one valid signed response (trust-on-first-signed), so a spoke never hard-fails against a hub that has not yet shipped signing. Operators upgrade hubs before enforcing on spokes. New pkg/spoke holds the shared signing contract and the spoke-side verifier; pkg/hub signs on the response path and verifies on the client path. Updates src/docs/security-self-assessment.md to mark the #7082 threat mitigated and state the new hub↔spoke trust model. Fixes #7082 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Summary
Adds the CNCF TAG-Security self-assessment for Hive under
projects/hive-commons/security-assessment/.What Hive is, and why the threat model is unusual
Hive orchestrates fleets of AI coding agents that autonomously maintain software projects — filing issues, opening pull requests, reviewing, and at high autonomy levels merging. Agents run as CLI subprocesses under tmux, in containers or pods, holding GitHub App or PAT credentials.
That means the system runs model output with credentials that can write to source repositories, so the assessment focuses on the boundaries that contain that rather than on a conventional network/data perimeter.
Covered:
ioscan— prompt-injection scanning of untrusted input on the kick path, with canary support0600, distinct from the OAuth/OIDC login pathWeaknesses are stated, not omitted
A self-assessment that claims everything is fine is not useful to a reviewer, so the document names its own gaps. The three most significant:
ioscan.fail_modedefaults toopen. A scanner or classifier outage degrades toward continuing to process untrusted input rather than blocking — a deliberate availability/security tradeoff, documented as such.It also records plainly that no third-party security audit or penetration test has been performed, and that SBOM/provenance attestations are currently disabled on image builds (with the reason, which is a real container-runtime exec failure rather than an oversight).
Notes for reviewers
hivecommons/hive@v4after the org move; one pre-existing broken path (docs/landscape.md→src/docs/landscape.md) is fixed here.hive-commons(for examplehive, or holding the file until the Sandbox application is decided), say the word and I will move it.References