Skip to content

Fix conflict scanning indexes the wrong history array - #7029

Merged
tim-smart merged 5 commits into
mainfrom
audit/repro-17f0b91a-unencrypted-eventlog-conflict-index
Aug 5, 2026
Merged

Fix conflict scanning indexes the wrong history array#7029
tim-smart merged 5 commits into
mainfrom
audit/repro-17f0b91a-unencrypted-eventlog-conflict-index

Conversation

@fubhy

@fubhy fubhy commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

The unencrypted server identifies the correct newer suffix of stored entries but scans an equally sized prefix when building conflicts. It can report entries older than the incoming event while omitting newer same-key entries from the end of storage.

Important

This PR starts with focused failing reproduction tests. Add the implementation fix to this same branch; CI is expected to fail until that fix is included.

Conflict scanning indexes the wrong history array

Module: effect/unstable/eventlog/EventLogServerUnencrypted
Audit ID: effect-64c8be87a9a44e92
Severity / confidence: high / high

What happens

The unencrypted server identifies the correct newer suffix of stored entries but scans an equally sized prefix when building conflicts. It can report entries older than the incoming event while omitting newer same-key entries from the end of storage.

Why it happens

At insertion index i, toConflicts assigns newHistory = history.slice(i). The loop is bounded by newHistory.length but reads history[j] instead of newHistory[j], so whenever i > 0 it examines prefix indices 0..newHistory.length - 1 rather than suffix indices i..history.length - 1.

Expected behavior

For each incoming entry, conflicts must contain all and only non-duplicate stored entries at or after its chronological insertion point whose event tag and primary key match the incoming entry.

Relevant implementation

These links and excerpts are pinned to audit base 17f0b91a243ccfe4a38d27debdc983adf434e738.

View problematic code at packages/effect/src/unstable/eventlog/EventLogServerUnencrypted.ts:396-423
const toConflicts = (
  history: ReadonlyArray<Entry>,
  originEntry: Entry
): [duplicate: boolean, conflicts: Array<Entry>, newHistory: Array<Entry>] => {
  let duplicate = false

  for (let i = 0; i < history.length; i++) {
    const entry = history[i]
    if (entry.createdAtMillis < originEntry.createdAtMillis) {
      continue
    } else if (entry.idString === originEntry.idString) {
      duplicate = true
      continue
    }

    const newHistory = history.slice(i)
    let conflicts: Array<Entry> = []
    for (let j = 0; j < newHistory.length; j++) {
      const scannedEntry = history[j]!
      if (scannedEntry.event === originEntry.event && scannedEntry.primaryKey === originEntry.primaryKey) {
        conflicts.push(scannedEntry)
      }
    }
    return [duplicate, conflicts, newHistory]
  }

  return [duplicate, [], []]
}

View exact lines on GitHub

Reproduction

pnpm test --run packages/effect/test/unstable/eventlog/UnencryptedEventLogConflictIndex.test.ts

Observed failure: Focused contract assertion failed against 17f0b91, demonstrating: Conflict scanning indexes the wrong history array.

Implementation handoff

The initial reproduction tests on this branch are the regression specification for the implementation fix that should follow in this PR.

  1. Start with the pinned implementation excerpts and the Why it happens analysis above.
  2. Change the implementation so it satisfies the stated Expected behavior; do not weaken or remove the reproduction assertions.
  3. Run the focused reproduction command(s) and confirm the observed failures become passing tests:
pnpm test --run packages/effect/test/unstable/eventlog/UnencryptedEventLogConflictIndex.test.ts
  1. Run the affected package's existing tests, then the repository lint and type checks before requesting review.

Audit provenance

  • Audit base: 17f0b91a243ccfe4a38d27debdc983adf434e738
  • Reproduction base: 17f0b91a243ccfe4a38d27debdc983adf434e738
  • Findings: effect-64c8be87a9a44e92
  • Initial patch: focused reproduction tests; implementation fix pending

Closes EFF-469

@fubhy fubhy added the audit Findings originating from the Effect runtime correctness audit label Aug 5, 2026
@changeset-bot

changeset-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 43e6c61

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pullfrog pullfrog 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.

Important

This PR currently contains only the reproduction test; the implementation fix it documents is not yet present. CI is expected to fail until toConflicts is fixed, so treat this review as feedback on the regression test and the path to the fix.

Reviewed changes

  • Added packages/effect/test/unstable/eventlog/UnencryptedEventLogConflictIndex.test.ts, a focused regression test that exercises EventLogServerUnencrypted.processEntries through the EventLog.WriteSingle RPC path.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread packages/effect/test/unstable/eventlog/EventLogServerUnencrypted.test.ts Outdated

@pullfrog pullfrog 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.

Important

The incremental change moves and adapts the regression test, but the toConflicts source fix is still absent on this branch and the new test file is not formatted.

Reviewed changes

  • Moved the reproduction from UnencryptedEventLogConflictIndex.test.ts into EventLogServerUnencrypted.test.ts and adapted registerHandlerUnsafe to the current { event, handler } API shape.
  • Verified the test still fails against the unfixed implementation with the expected [oldSameKey] vs [newerSameKey] mismatch.

⚠️ Source fix still missing and test file unformatted

The implementation fix for toConflicts is not present: packages/effect/src/unstable/eventlog/EventLogServerUnencrypted.ts:414 still reads history[j] instead of newHistory[j]. The new test file also fails pnpm dprint check — run pnpm dprint fmt on it before the next push.

The existing review thread on the line-106 assertion about also capturing originA's conflicts remains open and would make the regression test strictly stronger.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • Applied the one-line source fix in packages/effect/src/unstable/eventlog/EventLogServerUnencrypted.ts so toConflicts scans newHistory[j] instead of history[j], matching the suffix it intended to examine.
  • Strengthened the regression test in packages/effect/test/unstable/eventlog/EventLogServerUnencrypted.test.ts to assert conflicts for both originA (expected []) and originB (expected [newerSameKey.idString]), covering both the false-positive and false-negative sides of the bug.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 7.06 KB 7.06 KB 0.00 KB (0.00%)
batching.ts 9.86 KB 9.86 KB 0.00 KB (0.00%)
brand.ts 6.34 KB 6.34 KB 0.00 KB (0.00%)
cache.ts 10.71 KB 10.71 KB 0.00 KB (0.00%)
config.ts 20.73 KB 20.73 KB 0.00 KB (0.00%)
differ.ts 20.31 KB 20.31 KB 0.00 KB (0.00%)
http-client.ts 21.53 KB 21.53 KB 0.00 KB (0.00%)
logger.ts 10.84 KB 10.84 KB 0.00 KB (0.00%)
metric.ts 8.98 KB 8.98 KB 0.00 KB (0.00%)
optic.ts 7.18 KB 7.18 KB 0.00 KB (0.00%)
pubsub.ts 14.99 KB 14.99 KB 0.00 KB (0.00%)
queue.ts 11.66 KB 11.66 KB 0.00 KB (0.00%)
schedule.ts 10.83 KB 10.83 KB 0.00 KB (0.00%)
schema-class.ts 19.27 KB 19.27 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 29.09 KB 29.09 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.40 KB 25.40 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.42 KB 13.42 KB 0.00 KB (0.00%)
schema-string.ts 10.95 KB 10.95 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.21 KB 15.21 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 22.02 KB 22.02 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.45 KB 24.45 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.28 KB 19.28 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 19.11 KB 19.11 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.97 KB 18.97 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.69 KB 22.69 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.60 KB 19.60 KB 0.00 KB (0.00%)
schema.ts 18.52 KB 18.52 KB 0.00 KB (0.00%)
stm.ts 12.63 KB 12.63 KB 0.00 KB (0.00%)
stream.ts 9.80 KB 9.80 KB 0.00 KB (0.00%)

Comment thread packages/effect/test/unstable/eventlog/EventLogServerUnencrypted.test.ts Outdated

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • Added .changeset/tidy-cats-smile.md with a patch-level changeset describing the unencrypted event log conflict-scanning fix.
  • Removed the redundant Effect.scoped wrapper from the regression test in packages/effect/test/unstable/eventlog/EventLogServerUnencrypted.test.ts; it.effect already applies Effect.scoped internally, so the wrapper only created an unnecessary child scope.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@tim-smart
tim-smart merged commit eaa7e71 into main Aug 5, 2026
20 checks passed
@tim-smart
tim-smart deleted the audit/repro-17f0b91a-unencrypted-eventlog-conflict-index branch August 5, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

audit Findings originating from the Effect runtime correctness audit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants