Skip to content

docs(audit): record six verified-rejected findings; correct the #693 invariant - #838

Merged
QuantumExplorer merged 2 commits into
developfrom
claude/audit-non-issues-2026-08-26
Sep 6, 2026
Merged

docs(audit): record six verified-rejected findings; correct the #693 invariant#838
QuantumExplorer merged 2 commits into
developfrom
claude/audit-non-issues-2026-08-26

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Aug 26, 2026

Copy link
Copy Markdown
Member

Records the rejected findings #691, #700, #702, #703, #682, and #721, with their caller and trust-boundary assumptions, so later audits can distinguish duplicate claims from different failure modes.

Corrects the #693 rationale: non-Merk verification can consume proof-supplied metadata before parent authentication, but the bulk/commitment completed-chunk conversion is bounded independently. The validated height is in 1..=16, so completed_chunks = total_count / 2^height <= 2^63 - 1 for every u64 total_count. This conversion cannot overflow; its safety does not depend on release wrapping.

Clarifies two other contracts:

  • Lower-layer cost-version checks precede child writes, but direct typed appends can leave child writes in a caller-supplied transaction when a later parent update fails. The caller must roll back on error; locally owned transactions commit only on success.
  • Disabling grovedbg leaves its build script inactive and its optional build dependencies unactivated through that feature. Those dependencies still participate in workspace lockfile resolution.

Rejections are scoped to the documented claim, caller, input trust boundary, and applicable version. Independent MMR arithmetic, pagination, and resource use before authentication require separate analysis.

Documentation only. Validation: checked the corrected statements against the PR-head source and Cargo resolver documentation; verified the patch with git diff --check. No new runtime test run was needed for this prose-only update.

…invariant

Closes #691, #700, #702, #703, #682, #721 — all triaged as not reachable,
by-design, or hygiene, and closed on GitHub with the same rationales.

Adds two sections:

- Trust-boundary findings (#682 lazy-loaded Merk metadata, #700
  delete_checkpoint) — corrupt local storage and caller-chosen paths are
  inside the trust boundary; integrity auditing is delegated to
  Merk::verify / verify_grovedb on purpose. The #682 entry also records the
  argument against "fixing" it: validating hash_for_link on every link load
  adds hash_node_calls to the hot read path, and cost changes are
  replay-critical.
- Version-gating and build hygiene (#702 GroveVersion::default, #703
  missing version gates, #721 grovedbg build download).

#691 joins the existing proof/encoding section: a dense root-only proof does
verify in isolation, but every in-repo verifier either binds it to a query or
discards entries and forces the root to the parent-committed hash.

Also corrects a factual error that several entries leaned on. The overflow
section claimed element counts are "hash-verified before the arithmetic
runs". They are not: the non-Merk lower-layer verifiers are dispatched with
an Element decoded from the proof's own value_bytes, and combine_hash binds
it only afterwards. #693 stays closed on impact — the release build wraps and
the chain check rejects the result — but the stated reason was wrong, and a
build with overflow-checks enabled panics rather than wrapping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 7 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: cc540ccd-a01a-4243-862b-37d14174a9c7

📥 Commits

Reviewing files that changed from the base of the PR and between 6b34ea8 and af1f5a6.

📒 Files selected for processing (1)
  • docs/audit-non-issues.md

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Found three documentation-correctness issues. Most importantly, the corrected #693 rationale still identifies the wrong verifier invariant: the validated bulk-tree height bounds completed_chunks below the multiplication overflow threshold, so the asserted release wrap and overflow-check panic cannot occur. The transaction and optional-dependency statements also need narrower wording. Focused bulk-append, dense-tree, and version tests passed (367 tests), and git diff --check passed.

Comment thread docs/audit-non-issues.md Outdated
decoded from the proof's own `value_bytes`, and only at `verify.rs:2018`
does `combine_hash(value_hash(value_bytes), &lower_hash)` check it against
the parent-committed hash. A forged count therefore *does* reach the math;
what makes that safe is that the release build wraps and the chain check

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

[P2] Use the actual verifier bound for #693

verify_and_compute_root rejects height outside 1..=16 before this calculation. Therefore chunk_item_count = 2^height is at least 2, so for every u64 total_count, completed_chunks <= floor(u64::MAX / 2) = 2^63 - 1. Consequently 2 * completed_chunks - popcount(completed_chunks) cannot overflow. Neither release wrapping nor an overflow-checks = true panic is reachable on this verifier path. Please document this bound and remove the wrap/panic assertions; as written, the audit guide records behavior that cannot occur and the unchanged “Becomes real if” rule immediately matches the newly described unauthenticated input.

Comment thread docs/audit-non-issues.md Outdated
| Issue | Claim | Why not real |
|---|---|---|
| [#702](https://github.com/dashpay/grovedb/issues/702) (closed) | `GroveVersion::default` creates protocol version 0, satisfying v0 gates | Inert on two independent grounds. (1) `protocol_version` is **never read by any logic in the workspace** — grep finds it only in the four version-constant definitions and in test assertions; every gate reads a *feature* slot such as `grove_version.grovedb_versions.operations.insert.*`. (2) `GroveVersion::default()` is behaviorally identical to `GROVE_V1`: every `FeatureVersion` in V1 is `0`, and the only non-zero values in `v1.rs` are `protocol_version: 1` (never read) and `max_aggregate_sum_query_elements_scanned: 1024`, which is not a version slot and whose hand-written `impl Default` returns the same 1024. So the worst outcome is "the caller got V1 behavior", which is already legal via an explicit `GROVE_V1`. |
| [#703](https://github.com/dashpay/grovedb/issues/703) (closed) | Public versioned APIs miss explicit version gates and could execute writes instead of returning `VersionError` | Three of the seven cited refs are stale line numbers resolving to `root_key`, `root_hash` and `verify_grovedb` — two accessors and a verification helper, none version-dependent, none a write path; gating them would break callers on any unrelated slot bump. The four typed non-Merk append entry points (`mmr_tree_append`, `bulk_append`, `dense_tree_insert`, `commitment_tree_insert`) do lack a *top-level* gate but already fail closed deeper and **before any mutation**, via their cost dispatchers (`grovedb-merkle-mountain-range/src/cost/mod.rs:88-98`, `grovedb-bulk-append-tree/src/cost/mod.rs:182-201`, `grovedb-dense-fixed-sized-merkle-tree/src/tree/root_maintenance/mod.rs:56-71`, `grovedb-commitment-tree/src/commitment_tree/cost/mod.rs:43,73`). Writes also land in a `StorageBatch` committed only on success. The genuine residual is narrower and different: those four types have no `element_creation` gate of the kind `PrivateDocumentStore` uses (`grovedb_versions.rs:180-186`, enabled only in `v4.rs:407`) — a design-consistency question about when they become *creatable*, worth filing separately if wanted. |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

[P2] Qualify the StorageBatch atomicity claim

The direct typed append operations commit their child data_batch into the transaction before updating the parent Merk; the source comments explicitly say a later parent-update failure leaves orphaned subtree data in the transaction and that a caller-supplied transaction must be rolled back. For example, mmr_tree_append can pass the MMR cost gate, stage and commit its child writes, and then fail a later parent insert_subtree version gate. The early lower-layer dispatcher supports the narrow argument, but “committed only on success” is not generally true. Please qualify this to locally owned transactions/the caller rollback contract, or remove the sentence and narrow the “Becomes real if” condition.

Comment thread docs/audit-non-issues.md Outdated
|---|---|---|
| [#702](https://github.com/dashpay/grovedb/issues/702) (closed) | `GroveVersion::default` creates protocol version 0, satisfying v0 gates | Inert on two independent grounds. (1) `protocol_version` is **never read by any logic in the workspace** — grep finds it only in the four version-constant definitions and in test assertions; every gate reads a *feature* slot such as `grove_version.grovedb_versions.operations.insert.*`. (2) `GroveVersion::default()` is behaviorally identical to `GROVE_V1`: every `FeatureVersion` in V1 is `0`, and the only non-zero values in `v1.rs` are `protocol_version: 1` (never read) and `max_aggregate_sum_query_elements_scanned: 1024`, which is not a version slot and whose hand-written `impl Default` returns the same 1024. So the worst outcome is "the caller got V1 behavior", which is already legal via an explicit `GROVE_V1`. |
| [#703](https://github.com/dashpay/grovedb/issues/703) (closed) | Public versioned APIs miss explicit version gates and could execute writes instead of returning `VersionError` | Three of the seven cited refs are stale line numbers resolving to `root_key`, `root_hash` and `verify_grovedb` — two accessors and a verification helper, none version-dependent, none a write path; gating them would break callers on any unrelated slot bump. The four typed non-Merk append entry points (`mmr_tree_append`, `bulk_append`, `dense_tree_insert`, `commitment_tree_insert`) do lack a *top-level* gate but already fail closed deeper and **before any mutation**, via their cost dispatchers (`grovedb-merkle-mountain-range/src/cost/mod.rs:88-98`, `grovedb-bulk-append-tree/src/cost/mod.rs:182-201`, `grovedb-dense-fixed-sized-merkle-tree/src/tree/root_maintenance/mod.rs:56-71`, `grovedb-commitment-tree/src/commitment_tree/cost/mod.rs:43,73`). Writes also land in a `StorageBatch` committed only on success. The genuine residual is narrower and different: those four types have no `element_creation` gate of the kind `PrivateDocumentStore` uses (`grovedb_versions.rs:180-186`, enabled only in `v4.rs:407`) — a design-consistency question about when they become *creatable*, worth filing separately if wanted. |
| [#721](https://github.com/dashpay/grovedb/issues/721) (closed) | `grovedbg` build script downloads a release artifact at build time | Acceptable posture. Gated off by default (`default = ["full", "estimated_costs"]`; with `grovedbg` off, `build.rs` compiles to a literal no-op and `reqwest`/`sha2` are not even resolved), and integrity is enforced by a pinned version tag plus a pinned `GROVEDBG_SHA256` asserted at `build.rs:33`. A substituted, tampered, or 404 artifact fails the build — fail-closed. Real but minor DX defect not named in the issue: the `if !grovedbg_zip_path.exists()` guard at `:15` caches a bad download, so every later rebuild fails the SHA assert without re-fetching until `target/` is cleared. |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

[P3] Say optional dependencies are not activated, not unresolved

Cargo still resolves optional dependencies while producing the workspace lockfile even when their features are disabled. A feature-off focused test run here generated a Cargo.lock containing both reqwest and sha2; they are absent from the active feature-off build graph, so the accurate claim is that they are not activated, built, or executed. “Not even resolved” overstates the default-feature isolation.

@QuantumExplorer
QuantumExplorer merged commit 5966e62 into develop Sep 6, 2026
6 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/audit-non-issues-2026-08-26 branch September 6, 2026 22:14
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