Skip to content

[PM-29717] Fix event duplication by replacing KVStore checkpoint with solnlib#189

Draft
AlexRubik wants to merge 2 commits into
mainfrom
dirt/pm-29717/fix-checkpoint-duplication
Draft

[PM-29717] Fix event duplication by replacing KVStore checkpoint with solnlib#189
AlexRubik wants to merge 2 commits into
mainfrom
dirt/pm-29717/fix-checkpoint-duplication

Conversation

@AlexRubik
Copy link
Copy Markdown

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-29717

📔 Objective

The Bitwarden Splunk app duplicates event data on every scheduled run because the KVStore-based checkpoint mechanism fails to persist in Splunk Cloud environments. The checkpoint either doesn't initialize (collection not created) or doesn't persist data (nested object serialization issues), causing every run to re-fetch and re-index the same events from the original time window.

This PR:

  1. Replaces the custom KVStore checkpoint code with solnlib.modular_input.checkpointer.KVStoreCheckpointer, a battle-tested library (already bundled via splunktaucclib) that handles collection initialization, error recovery, and Splunk Cloud compatibility
  2. Adds API rate limiting — 200ms delay between paginated API calls (5 calls/sec), staying safely under Bitwarden's 400 calls/minute limit

What changed

File Change
src/config.py Replaced custom KVStore read/write with solnlib KVStoreCheckpointer; flattened checkpoint data (no nested objects); added legacy migration from old eventsapi collection
src/models.py Removed key_id field from EventLogsCheckpoint (now managed by solnlib)
src/event_logs.py Added time.sleep(0.2) between paginated API calls for rate limiting
tests/test_config.py Added 7 unit tests for checkpoint serialization/deserialization roundtrip

Related issues

Out of scope — follow-up items

The following improvements are planned but not included in this PR to keep the change focused:

  • Batch page limit per run — Cap each invocation at ~200 API pages to prevent Splunk timeout kills on large backfills. The checkpoint mechanism ensures the next run picks up where this one stopped.
  • Explicit checkpoint error handling in App.run() — If checkpoint update fails after events are already indexed, stop immediately instead of continuing to write uncheckpointed events.
  • Deprecate/remove legacy eventsapi KVStore collection from collections.conf once migration is confirmed working.

⚠️ Testing status

Completed

  • Unit tests for checkpoint serialization/deserialization (7 tests, all passing)
  • Local Splunk (Docker) end-to-end testing with a date-aware mock Bitwarden API:
    • Verified solnlib checkpoint persists and advances correctly
    • Verified rate limiting (~200ms between API calls vs ~20ms before)
    • Verified legacy checkpoint migration from old eventsapi collection
    • Verified no duplicate events on subsequent runs when checkpoint is intact

Still needed

  • Splunk Cloud environment testing — The core bug (KVStore collection not initializing) only manifests in Splunk Cloud. Local Splunk's KVStore works fine with both old and new code. Need to validate that solnlib.KVStoreCheckpointer correctly auto-creates the collection and persists data in an actual Splunk Cloud deployment.
  • Testing with real Bitwarden organization — Validate with real API pagination, continuation tokens, and event volumes.

📸 Screenshots

Not applicable — no UI changes.

The custom KVStore checkpoint code fails in Splunk Cloud environments
because the collection may not initialize and nested object serialization
doesn't round-trip correctly. Replaces with solnlib's KVStoreCheckpointer
which handles initialization, error recovery, and Cloud compatibility.

Flattens checkpoint data structure to avoid nested object issues.
Removes key_id from EventLogsCheckpoint model (now managed by library).
Includes migration from legacy 'eventsapi' KVStore collection.
Adds checkpoint serialization roundtrip tests.

[PM-29717]
The read_events loop previously made API calls as fast as the network
allowed (~10/sec), triggering Bitwarden's 429 rate limit. Adds a 200ms
delay between paginated calls (5 calls/sec, under the 400/min limit).

[PM-29717]
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

Logo
Checkmarx One – Scan Summary & Details5d116f3b-a983-46c6-bd33-a4da0a56301f


New Issues (9) Checkmarx found the following issues in this Pull Request
# Severity Issue Source File / Package Checkmarx Insight
1 HIGH CVE-2026-33671 Npm-picomatch-2.3.1
detailsRecommended version: 2.3.2
Description: `picomatch` is vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob ...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
2 HIGH CVE-2026-33671 Npm-picomatch-4.0.3
detailsRecommended version: 4.0.4
Description: `picomatch` is vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob ...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
3 HIGH CVE-2026-33891 Npm-node-forge-1.3.3
detailsRecommended version: 1.4.0
Description: A Denial of Service (DoS) vulnerability exists in the node-forge library due to an infinite loop in the "BigInteger.modInverse()" function (inherit...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
4 HIGH CVE-2026-33894 Npm-node-forge-1.3.3
detailsRecommended version: 1.4.0
Description: Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, RSASSA PKCS#1 v1.5 s...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
5 HIGH CVE-2026-33895 Npm-node-forge-1.3.3
detailsRecommended version: 1.4.0
Description: Ed25519 signature verification accepts forged non-canonical signatures where the scalar S is not reduced modulo the group order (S >= L). A valid s...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
6 HIGH CVE-2026-33896 Npm-node-forge-1.3.3
detailsRecommended version: 1.4.0
Description: `pki.verifyCertificateChain()` does not enforce RFC 5280 basicConstraints requirements when an intermediate certificate lacks both the `basicConstr...
Attack Vector: NETWORK
Attack Complexity: HIGH
Vulnerable Package
7 HIGH CVE-2026-4867 Npm-path-to-regexp-0.1.12
detailsDescription: A bad regular expression is generated any time you have three or more parameters within a single segment, separated by something that is not a peri...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
8 HIGH CVE-2026-4926 Npm-path-to-regexp-0.1.12
detailsRecommended version: 0.1.13
Description: A bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as "{a}{b}{c}:z". The genera...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package
9 HIGH CVE-2026-4926 Npm-path-to-regexp-8.3.0
detailsRecommended version: 8.4.0
Description: A bad regular expression is generated any time you have multiple sequential optional groups (curly brace syntax), such as "{a}{b}{c}:z". The genera...
Attack Vector: NETWORK
Attack Complexity: LOW
Vulnerable Package

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