HarperFast / HarperFast/harper

addTo/subtractFrom accumulation bypasses declared column type bounds (Int 32-bit, Long ±2^53) at commit-merge

Open
#1,372 2 comments 0 reactions 0 assignees View on GitHub
area:storage
Dominant language
JavaScript
Stars
89
Forks
10
Avg merge
2d 6h
Merged PRs (30d)
200

Description

## Summary

Incremental `addTo`/`subtractFrom` operations silently commit values that exceed a column's declared type bound. A single large delta that itself exceeds the bound IS correctly rejected — but the accumulated running total escapes validation entirely.

**Confirmed affected types (both engines, no divergence):**
- `Int` (32-bit): `addTo(1)` on a column holding `2^31−1` → commits `2147483648` with no error, no wrap
- `Int` underflow: `subtractFrom(1)` on `−2^31+1` → commits `−2147483649`
- `Long` (±2^53): two `addTo(2^53)` calls → commits `2^54 = 18014398509481984`

**Correct (unaffected):** SQL `UPDATE col = col + 1` correctly rejects the out-of-range resolved value (calls `set()` with the final computed value).

## Root cause

`Addition.update(prev) { return (+prev||0) + this.value }` at `resources/tracked.ts:590` returns the summed result directly to the storage layer **without routing through `set()`**. The per-delta validator in `set()` (≈82–97) validates `value.__op__ ? value.value : value` — i.e. the delta — which passes for any single in-range increment. The commit-merge path is unguarded.

## Impact

Any fleet using a typed `Int` column to cap a counter or quota (e.g. 32-bit sequence, rate-limit counter) or relying on the `Long` ±2^53 bound under `addTo`/`subtractFrom` workloads does not get that cap. Values silently exceed the declared range with no error and no data corruption (values stay exact JS-safe integers). The only safe workaround today is using SQL arithmetic UPDATE (which enforces correctly) or an open/untyped column.

## Fix

Re-validate the resolved sum against the column's type bound in `tracked.ts:590` — call the column validator with the committed result after merge, or inline the bound check.

## Repro

```
# Int overflow (F-018)
npm run test:integration -- "integrationTests/qa-scratch/qa110-atomic-edges.test.ts" # probe 6a
HARPER_STORAGE_ENGINE=lmdb npm run test:integration -- "integrationTests/qa-scratch/qa110-atomic-edges.test.ts"

# Int underflow + Long accumulation
npm run test:integration -- "integrationTests/qa-scratch/qa112-int-bound.test.ts"
HARPER_STORAGE_ENGINE=lmdb npm run test:integration -- "integrationTests/qa-scratch/qa112-int-bound.test.ts"
```

Harper commit: `7aaa5a152` (branch `kris/repl-no-revalidate-1302-core`; root cause in `tracked.ts` is branch-independent).

*Found by the automated QA explorer campaign (waves 33–34). 🤖 Generated with [Claude Code](https://claude.com/claude-code)*

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.