Skip to content

fix(import): the parsers stop trusting the file and the Hevy API - #350

Merged
DuarteSantos8 merged 1 commit into
DuarteSantos8:mainfrom
kurktchiev:gh/importers-hostile
Oct 7, 2026
Merged

DuarteSantos8 merged 1 commit into
DuarteSantos8:mainfrom
kurktchiev:gh/importers-hostile

Conversation

@kurktchiev

Copy link
Copy Markdown
Contributor

Six ways an export file or a Hevy API response put something in the store that the app then counts or shows wrong. Each one has a test that fails on the old parser.

  1. A Hevy exercise whose sets have nothing measured. When every set of an exercise has no weight, reps, time or distance, import-hevy.js ~228 (if (!day.ex.has(id)) day.ex.set(id, [])) has already filed it, so it imports as an entry with no sets. It counts as matched, an unmatched one is added to the Library as "your own", and a day with nothing else on it becomes an empty workout. That empty workout then blocks the real session on a later import, because days already in the history win the merge. The exercise is filed only once a set lands in it, a day with no entries is dropped, and the matched / created / "added as your own" figures are built from the ids the parse kept.
  2. A missing start time. new Date(null) is the epoch, so a Hevy workout with start_time: null was filed under 1 January 1970. localWhen accepts only a string now, and the session is skipped and counted like any other bad start.
  3. An Apple Health weight of ".". The value pattern [\d.]+ lets a bare "." through, and parseFloat makes it a NaN weigh-in; a "0" became a zero one. Both are skipped, which is what the CSV branch below it already did.
  4. A Hevy workout title that is not a string reached the stored name, and the history list renders it as it is.
  5. A Hevy distance_meters that is not a number divided into a NaN speed.
  6. CATEGORY_BP, KM and HEVY_BP were plain objects indexed by text from the file or the API. A category of constructor stored a function as the body part and __proto__ stored Object.prototype. They have no prototype now.

The CSV importer files an exercise and pushes its set in the same step, so it never had the empty-entry case.

import-csv.hostile.test.js and import-hevy.hostile.test.js throw malformed and adversarial input at both parsers: wrong types, prototype names, garbage dates, thousands of rows, files that are not exports. 42 tests pass, and 14 of them fail on the old parsers.

The existing import tests still pass (import-csv, import-csv.strong, import-effort, import-hevy, import-match, import-name-bodypart: 90), as do sheets.import-summary and sheets.plan-import-toast (4), history and sync-merge (222).

#312 edits both parsers and merges cleanly with this. #177 edits import-csv.js and already conflicts with main there.

🤖 Generated with Claude Code

Six ways an export file or a Hevy response put something into the
store that the app then showed or counted wrongly:

- A Hevy exercise whose sets all had nothing measured imported as an
  entry with no sets. It counted as matched, an unmatched one was added
  to the Library as your own, and a day with nothing else on it became
  an empty workout that blocked the real session on a later import,
  since existing days win in the merge. The exercise is filed only once
  a set lands in it, a day with no entries is dropped, and the matched,
  created and "added as your own" figures are built from the ids the
  parse kept.
- A Hevy workout with a null start_time was filed under 1970: new
  Date(null) is the epoch. localWhen takes only a string now, so the
  session is skipped and counted like any other bad start.
- An Apple Health record with value "." (the pattern allows it) became
  a NaN weigh-in, and "0" a zero one. Both are skipped, the gate the
  CSV branch already had.
- A Hevy title that is not text reached the stored workout name, which
  the history list renders as it is.
- A Hevy distance that is not a number divided into a NaN speed.
- CATEGORY_BP, KM and HEVY_BP were plain objects indexed by text from
  the file or the API, so a category of "constructor" stored a function
  as a body part and "__proto__" stored Object.prototype. They have no
  prototype now.

import-csv.hostile.test.js and import-hevy.hostile.test.js feed both
parsers malformed and adversarial input: 42 tests, 14 of them fail on
the old parsers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@DuarteSantos8
DuarteSantos8 merged commit e2d58b4 into DuarteSantos8:main Oct 7, 2026
4 checks passed
@DuarteSantos8

Copy link
Copy Markdown
Owner

Merged and released in v1.3.10, thanks @kurktchiev!

https://github.com/DuarteSantos8/openGym/releases/tag/v1.3.10

@kurktchiev
kurktchiev deleted the gh/importers-hostile branch October 9, 2026 15:34
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.

2 participants