Skip to content

Revert "fix(governance): replace unvalidated u8 discriminants with typed enums and bounds checks"#3032

Merged
chefsale merged 1 commit into
masterfrom
revert-2965-claude/u8-discriminants-validation-h83wtt
Jun 29, 2026
Merged

Revert "fix(governance): replace unvalidated u8 discriminants with typed enums and bounds checks"#3032
chefsale merged 1 commit into
masterfrom
revert-2965-claude/u8-discriminants-validation-h83wtt

Conversation

@chefsale

Copy link
Copy Markdown
Member

Reverts #2965

@chefsale chefsale marked this pull request as ready for review June 29, 2026 08:42
@cursor

cursor Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@chefsale chefsale merged commit 9a7c6d3 into master Jun 29, 2026
12 of 13 checks passed
@chefsale chefsale deleted the revert-2965-claude/u8-discriminants-validation-h83wtt branch June 29, 2026 08:43
@github-actions

Copy link
Copy Markdown

Your PR title does not adhere to the Conventional Commits convention:

<type>(<scope>): <subject>

Common errors to avoid:

  1. The title must be in lower case.
  2. Allowed type values are: build, ci, docs, feat, fix, perf, refactor, test.

@meroreviewer meroreviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 MeroReviewer

Reviewed by 1 agents | Quality score: 24% | Review time: 58.1s

🟡 2 warnings. See inline comments.


🤖 Generated by MeroReviewer | Review ID: review-f86e0211

let signer = ctx.signer();
ctx.settings().set_subgroup_visibility(signer, *mode)?;

let visibility = match *mode {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Unvalidated u8 discriminant for VisibilityMode uses catch-all default

The match 0 => Open, _ => Restricted means any non-zero byte received over the wire (e.g., 2, 255) is silently treated as Restricted. This is a silent data corruption risk: a malformed or future-extended op will be misinterpreted rather than rejected. The original typed enum with Borsh deserialization would have rejected unknown discriminants at the boundary. The same pattern appears in store_subgroup_visibility.rs.

Suggested fix:

Either restore the typed VisibilityMode enum with BorshDeserialize (which rejects unknown discriminants), or add an explicit error branch: `0 => Open, 1 => Restricted, other => return Err(eyre::eyre!("unknown visibility discriminant: {}", other))`.

) -> Self::Result {
let result =
CapabilitiesRepository::new(&self.datastore).set_subgroup_visibility(&group_id, mode);
let visibility = match mode {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Unvalidated u8 discriminant for VisibilityMode with silent fallback

The match 0 => Open, _ => Restricted silently maps any unrecognized byte to Restricted. This is the same issue as in subgroup_visibility_set.rs — a future discriminant value or a corrupted byte will be silently misinterpreted rather than rejected with an error.

Suggested fix:

Add an explicit error arm for unrecognized discriminants rather than using a catch-all `_` pattern.

@meroreviewer

meroreviewer Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Documentation Review

The following documentation may need updates based on the changes in this PR:

  • 🟡 AGENTS.md: AGENTS.md exists but was not updated — consider updating it to reflect the architecture changes in this PR.
  • 🟡 CONTRIBUTING.md: CONTRIBUTING.md exists but was not updated — consider updating it to reflect the architecture changes in this PR.
  • 🟡 architecture/auto-follow.html: Files matching crates/governance-store/** were changed but architecture/auto-follow.html was not updated (per source_to_docs_mapping).
  • 🟡 architecture/crates/context.html: Files matching crates/context/** were changed but architecture/crates/context.html was not updated (per source_to_docs_mapping).
  • 🟡 architecture/crates/dag.html: Files matching crates/op-adapter/** were changed but architecture/crates/dag.html was not updated (per source_to_docs_mapping).
  • 🟡 architecture/local-governance.html: Files matching crates/governance-store/** were changed but architecture/local-governance.html was not updated (per source_to_docs_mapping).
  • 🟡 architecture/membership-and-leave.html: Files matching crates/context/** were changed but architecture/membership-and-leave.html was not updated (per source_to_docs_mapping).
  • 🟡 docs/: Static HTML docs in docs/ may need updating — architecture-impacting changes detected. On merge, update-docs will scan this directory and open a PR if any pages need to change.

@meroreviewer meroreviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 AI Code Reviewer

Reviewed by 2 agents | Quality score: 34% | Review time: 174.4s

🟡 2 warnings. See inline comments.


🤖 Generated by AI Code Reviewer | Review ID: review-ae193f40

.context_member_capability(group_id, context_id, member)?
.unwrap_or(0);
caps.set_context_member(group_id, context_id, member, current | capability.get())?;
caps.set_context_member(group_id, context_id, member, current | capability)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Zero capability bitmask accepted without validation

With ContextCapabilityBits removed, the capability parameter is now a plain &u8. Nothing prevents a ContextCapabilityGranted op from being constructed or deserialized with capability == 0. Granting zero bits is a no-op that silently succeeds, but it also means a malformed or malicious op can be replayed without error. The original ContextCapabilityBits type rejected zero at Borsh deserialization time, providing a wire-level guard. Without it, a zero-capability grant op will be applied and stored, potentially masking bugs or enabling replay of semantically invalid ops.

Suggested fix:

Add an explicit guard at the top of the `apply` function: `if *capability == 0 { return Err(eyre::eyre!("capability bitmask must not be zero")); }`. This restores the invariant that was previously enforced by the `ContextCapabilityBits` newtype.

let signer = ctx.signer();
ctx.settings().set_subgroup_visibility(signer, *mode)?;

let visibility = match *mode {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Unvalidated u8 discriminant silently maps unknown values to Restricted

The match *mode { 0 => Open, _ => Restricted } pattern accepts any byte value other than 0 as Restricted. A malformed or future-extended op with an unknown discriminant (e.g. 2, 255) will be silently coerced to Restricted rather than being rejected. The original typed-enum approach caught this at Borsh deserialization time. The same silent-coercion pattern also appears in store_subgroup_visibility.rs.

Suggested fix:

Add an explicit arm for the only two valid values and return an error for anything else: `match *mode { 0 => Ok(VisibilityMode::Open), 1 => Ok(VisibilityMode::Restricted), other => eyre::bail!("unknown visibility discriminant: {other}") }`.

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.

1 participant